P3081: Core safety profiles (WG21, 2025)
Herb Sutter, Core safety profiles for C++26, document P3081R2, 2025-02-04, addressed to EWG and LEWG. T1 — a working-group paper published by the committee that governs C++, read from open-std.org.
The spoke's answer to the memory-safety question it has been asking cpp since that page was written: the language's own proposal for closing the gap rust opened.
The problem, as the committee states it
"Our problem 'is' the ease of writing code in C++ that inadvertently creates security vulnerabilities due to weak language safety guarantees, very often due to the lack of enforcement of practical, established best practices. The most urgent need is to address bounds, type (including initialization), and lifetime safety."
And, explicitly, what the problem is not: "needing formal provable language safety; or needing to convert all C++ code to memory-safe languages."
That is the fork in the road stated in one sentence. rust takes safety to be a property proven by the compiler; this paper takes it to be a set of enforced rules whose value is measured in CVEs prevented per unit of adoption pain.
The constraint that shapes everything
"We must maintain backward compatibility and not require changes to C++'s object model and lifetime model. Each enforced rule must be deterministically decidable at compile time in a way that is sufficiently efficient to implement in-the-box in the C++ implementation, including without unacceptable impact on compilation times."
Three consequences follow, and they are exactly what cpp-hopl predicted a consensus committee would produce. Rules must be enforced by the compiler, not a separate static analyser — the paper quotes David Chisnall's FreeBSD post on why: "it's far easier to prevent people from committing code that doesn't compile than it is to prevent them from committing code that raises static analysis warnings." Analysis with a proof obligation is out. And anything requiring a new object model is out before it starts, which is the ground on which the competing Safe C++ proposal (P3390, a borrow checker and a new dialect) was rejected.
The mechanism
Profiles — bounds, type (initialization merged into it in R1), lifetime, arithmetic — each a
set of rules a conforming implementation must enforce when the profile is enabled. Opt-out is the
interesting half:
void f(int i) {
(double*)&i; // error: type-unsafe
[[profiles::suppress(std::type)]]
(double*)&i; // ok
}
Suppression is granular — a statement or a block — and per profile, so saying "trust me" about a cast does not silently disable bounds checking on the same line.
Each rule is addressed by one of three tactics, ranked by what they cost the user:
| Tactic | What it does | Manual code changes |
|---|---|---|
| Fix | give the code the safe semantics, in both enforced and non-enforced mode | none |
| Reject | ill-formed, diagnostic required, ideally with a "use this instead" | yes |
| Check | diagnose at run time, with a customisable handler | none |
The design goal is stated as adoption economics rather than as a guarantee: "silently fix serious bugs
in existing code by just recompiling," with apply(P) as a zero-source-change level and a higher level
that may require edits.
What this is worth to the spoke
A fifth position on the safety axis. memory-management-model and type-system record rust preventing the bug in the compiler, elixir surviving it in the runtime, spark proving it cannot occur, and c declining the question. This is a sixth thing: retrofit enforcement into an existing language without changing its semantics, with adoptability as the primary design constraint and the guarantee explicitly partial.
And it is the compatibility constraint doing metaphysics. cpp-hopl showed that the C++ committee cannot break code because no participant can impose a migration cost on everybody else. Here that structural fact decides what safety can mean in the language: not a proof, not a dialect, but whatever can be checked in-the-box without touching the object model. The governance model wrote the technical answer.
Status matters and is not on this page's face. P3081 targets C++26 in its title. It did not make it — see wg21-croydon-2026: profiles were deferred to C++29, and what C++26 actually shipped for safety is the hardened standard library (cpp-library-hardening) plus the elimination of undefined behaviour for uninitialized locals.
Related
cpp · cpp-library-hardening · wg21-croydon-2026 · cpp-hopl · rust · memory-management-model · type-system · language-governance · spark