C++ safety profiles need an invariant their users can rely on
Jonathan Müller's P3649 distinguishes enforceable safety guarantees from useful heuristics. Its initialization example exposes the need to mark unsafe library abstractions.
Banning a dangerous-looking expression does not establish that the behavior it can cause has become impossible. A function can hide that expression and expose the same operation through an ordinary call. Jonathan Müller's safety-profile paper uses that problem to argue that a C++ profile needs a stated invariant and a way to preserve it across abstraction boundaries.
P3649R0, dated May 19, asks WG21 to prioritize profiles that provide hard guarantees. Müller distinguishes that goal from enforcing style rules or checks that reduce the likelihood of a bug. The paper proposes a direction for standardization, not an implemented safety mode that a C++26 build can already select.
I find RAII persuasive because it supplies a concrete rule about destruction on ordinary scope exit. A safety profile should be equally explicit about what it guarantees and where that guarantee applies. Otherwise a project can accept substantial source restrictions without knowing which invalid executions the restriction actually excludes.
A guarantee requires coverage of the forbidden behavior
Müller describes a profile in terms of an invariant over its covered code, which the paper calls its dominion. The implementation can enforce that invariant by rejecting code statically or by checking operations at runtime. Its mechanism can vary, but the claimed guarantee cannot quietly permit false negatives inside the covered region.
A heuristic has a different value. It can catch common mistakes without detecting every possible way to produce them. That can still be useful engineering work. The paper's objection is to describing the resulting improvement as an invariant when a conforming accepted program can still violate it through an unchecked route.
The distinction affects how restrictions should be justified. Prohibiting a construct because it can violate the invariant gives a technical reason that a user can evaluate. Prohibiting it because it is unfashionable does not establish the same property. Müller argues that the first approach also gives profile evolution a more stable target than continually adding stylistic restrictions.
This is a design argument, not a proof that the proposed framework already meets its requirements. The paper deliberately uses an example to expose a missing capability. Identifying the invariant is only the start of describing how every relevant operation preserves it.
Initialization is a useful test case
The paper considers a profile under which variables must be initialized before use. A simple enforcement strategy rejects default-initialized scalar declarations and operations that can create access to uninitialized storage. That can conservatively reject programs which are actually valid.
For example, a declaration followed by an assignment before use can satisfy the intended invariant. A rule that bans the declaration alone rejects it anyway. More sophisticated flow analysis could recognize the intervening assignment. The standards question is whether a profile requires one analysis strategy or permits implementations to accept more programs when they can establish the same guarantee.
False positives and false negatives consequently play different roles. Rejecting a valid program reduces usability or portability. Accepting a program that violates the invariant invalidates the guarantee itself. A profile can choose conservative rejection while its analysis improves, but it cannot describe missed violations as merely an inconvenient diagnostic detail.
Müller's example also excludes calls that return uninitialized storage, such as std::malloc. Once that rule exists, wrapping the call in a function named my_malloc should not make the same unsafe operation acceptable. Source-level syntax matching cannot resolve that abstraction problem on its own.
Suppression and an unsafe interface are different claims
P3649 proposes [[profiles::prohibit_in(P)]] to identify an abstraction that can violate profile P. It distinguishes that from [[profiles::suppress(P)]], which locally bypasses a restriction where the programmer takes responsibility for preserving the invariant. The spellings are proposed framework syntax, not attributes already required by the C++ standard.
A library can use a locally prohibited operation internally while still offering a safe interface. If the implementation establishes the required initialization before returning storage, it need not expose the same unsafe obligation to every caller. A wrapper that returns uninitialized storage has a different interface contract and needs the prohibition to remain visible outside its body.
That distinction is essential for useful libraries. If every internal use of low-level code propagated without qualification, safe abstractions could not conceal their implementation details. If no obligation propagated, a trivial wrapper could bypass the profile. The proposed annotation names which of those cases the interface presents.
Dependencies and callbacks create the same boundary issue. The paper treats code not covered by the profile as outside its guarantee. Its proposed import requirements let a user demand profile participation from dependencies. Such a requirement still needs an honest account of explicit suppressions and externally supplied code. This means that enabling a profile for one translation unit cannot automatically establish the invariant for every function that translation unit can call.
Library hardening addresses a narrower requirement
The adopted P3471R4 library hardening design supplies a useful comparison. It selects existing library preconditions for runtime checking and explicitly declines to check every requirement in the library. Its scope includes inexpensive bounds and state checks that implementations can deploy in production.
That is compatible with being valuable without constituting a complete initialization or memory-safety profile. A checked container index does not describe every way a program can acquire an invalid pointer. Müller's proposed profile standard asks a broader question about eliminating the entire specified class of invalid behavior within a covered region.
I favor this insistence on naming the guarantee because C++ programmers already have to distinguish many kinds of diagnostics and conventions. A warning policy, a hardened library, and a safety profile can all help, but a user needs to know the exact property each supplies. P3649 makes the abstraction boundary concrete. A function that exposes a prohibited operation must carry that obligation to its callers, while an implementation that preserves the invariant can keep the low-level operation inside its own reviewed boundary.