Making C++ Safe Is Not Optional

The Profiles framework brings language-level safety to C++: closing entire classes of undefined behavior at the source, and checking the rest.

Gabriel Dos Reis
Gabriel Dos ReisP3589 · C++ Profiles: The Framework
Bjarne Stroustrup
Bjarne StroustrupP4222 · initialization profile
A program that doesn’t violate the initialization profile runs exactly the same if the profile is enforced or not (or fails to compile if the profile isn’t enforced).
Bjarne Stroustrup, “An initialization profile” (P4222)

Nine aspects of the mechanism

01Initialization

Uninitialized reads are among the cheapest defects to write and among the most expensive to diagnose. An initialization profile makes them ill-formed: every object is initialized before it is read, a user-provided constructor must initialize every member, and the static initialization order fiasco becomes a compile error. Where raw storage is required, the hole is named.

02Bounds, and default-deny

A memory-safety profile starts from default-deny: an access is invalid unless the compiler can prove it belongs on the whitelist. When vector’s operator[] carries the bounds rule, every caller inherits the guarantee without annotating the call.

03Compile-time diagnosis of the common cases

“Unprovable in the general case” is not the same as “unaddressable in the cases that dominate production.” Profiles target what the compiler can see. Those cases become ill-formed. The residue is enumerated and handled as runtime.

04Incremental adoption on existing code

Profiles are adopted per translation unit. There is no flag day, and the annotations that are required are local, verifiable, and do not change semantics. A project can enforce initialization in new files this week and leave a device driver on a recorded suppress until it is ready.

05Named suppression

Safety that cannot be locally waived is safety that will be globally disabled. A suppress is named, scoped, and carries a recorded justification. The hole is greppable. Silence is not a dialect.

06Runtime checking for the remaining core-language UB

For the runtime-checkable residue, a profile enumerates the cases, owns the check, and owns the response. The argued default is termination. Continuation past core-language undefined behavior is not the portable guarantee. The goal is not merely to avoid executing past undefined behavior but to eliminate it, which requires that the checks themselves cannot be optimized away by the undefined behavior they guard.

07A first-class framework, not a configuration preset

A profile attests that these rules hold, or the program is ill-formed / does not proceed. That is a different object from a named bundle of switches over someone else’s machinery. A preset only selects from available settings; an attestor can reject the whole program.

08Localizing the annotation burden

Library authors do the local work once; applications consume it. The annotations belong in a few specific places: constructors that must initialize every member, bounds checks inside operator[], and lifetime transitions at the allocator boundary.

09One meaning across implementations

A profile is a portable language rule. The same source, under the same profile, is intended to be ill-formed or well-formed regardless of which vendor compiled it. If two implementations can disagree about whether a safety rule held, users have a vendor option, not a rule.

The Papers

NumberTitleFocus Area
P3589C++ Profiles: The Framework
Gabriel Dos Reis
Framework
P3970Profiles and Safety: a call to actionDirection
P4222An initialization profileInitialization
P4296Default-Deny + Provable-Whitelist InvalidationMemory Safety
P4297Severing P3100’s Profiles Claim from Its Case-by-Case ReviewArchitecture
P4306Configuring Runtime Checking: Profiles and Implicit Contract AssertionsConfiguration
P4308Eight Responses to a Throwing Implicit Contract AssertionContract Responses
P4318Transient Benefit, Perpetual Cost: Implicit Core-Language AssertionsCost Analysis
P4238Returning C++26 for the Evaluation It SkippedProcess

Try the Code

Eight worked examples of the initialization profile, live on Compiler Explorer.

Profiles-enabled Clang · -std=c++23 -fprofiles

// std::init: every object is initialized before it is read.
[[profiles::enforce(std::init)]];
struct Options { int timeout_ms; bool verbose; };
int run(int argc, const char **argv) {
    Options opts;   // error: indeterminate
    int retries;    // error: uninitialized
    return opts.timeout_ms * retries;
}

Why Not Contracts?

Contracts are a valuable tool for documentation, validation, and optimization. But they are not a safety net. Contracts can be disabled, ignored, or forgotten. Safety must not depend on programmer discipline or build modes.

Profiles bake safety into the language rules. They close entire classes of undefined behavior by construction, not convention.

Contracts can describe intent. Profiles guarantee it.

Limits of the alternative

01It can be switched off

An alternative that is stripped by a build mode is not a safety net. The person who ships the binary is often not the person who wrote the annotation. Discipline is not a mechanism.P3100R8

02A conforming implementation may do nothing

The leading paper for the alternative states that existing implementations are already conforming with its wording transformation. A vendor may treat the new checks as assumptions and generate the same code as today.P3100R8

03Continuation after a language-undefined state

Some designs invoke a handler and then continue. At the core-language-undefined class, continuation is execution in a state the language does not define. That is a protocol for living with undefined behavior, not closing it.P4318R1

04Coverage is a fraction of the census

By the alternative’s own census, only 20 of 82 enumerated core-undefined-behavior cases are unconditionally locally runtime-checkable. A facility that cannot see most of the problem should not be the substrate every other story sits on.P3100R8

05The static half is declared impossible, then used to close the door

The same census concludes that none of the 82 cases can be unconditionally diagnosed at compile time. “Unprovable in general” is not “unaddressable in the common cases.”P3100R8

06Ordinary code can change meaning

One documented example flips the result of a well-formed program: noexcept of an expression that would become a core-language check is no longer the noexcept programmers have been writing for a decade.P4308R1

07The menu is not a guarantee

The evaluation semantics are selected outside the source, by whoever owns the final build rather than the author of the code. C++26 defines four of them for explicit contract assertions; the proposal adds a fifth, assume, for the implicit assertions that guard core-language undefined behavior, letting the optimizer rely on a condition that was never checked.P4308R1

08Additive wording is not architecturally free

Additive at the wording level is how a substrate gets locked. Once every core-language check is routed through a single program-wide handler, every later safety feature inherits that routing.P4297R1

09Predicates are a weak static-analysis surface

Arbitrary predicates are attractive in slides and expensive in tools. A rule fixed by the language gives an analyzer one thing to reason about across every call site; an arbitrary boolean expression has to be understood a case at a time.

10Cost is paid in the rest of the language

A check that can throw can unwind, and unwinding is not free on every ABI. A check treated as an assumption can widen undefined behavior rather than close it. Cost studies leave the largest of these effects for future work.P4318R1

What They Say

I just want C++ to let me enforce our already-well-known safety rules and best practices by default, and make me opt out explicitly if that’s what I want.
Herb Sutter
Chair, ISO C++ Committee · “C++ safety, in context”
SG23 and EWG have repeatedly (by massive votes) pointed to “Profiles” as the direction for addressing these urgent needs … The way to make progress is to build on the proposed Profiles framework.
David Vandevoorde
Edison Design Group · lead author, P3970R0
To address the contemporary challenge of memory safety concerns, we need some standard profiles related to type and memory safety, guaranteed to be available in all C++ implementations.
Gabriel Dos Reis
Microsoft · author, P3589R3

Bibliography

  1. P3038R0Concrete suggestions for initial ProfilesBjarne Stroustrup · 2023-12-16
  2. P3100R8A framework for systematically addressing undefined behaviour in the C++ StandardTimur Doumler, Joshua Berne · 2026-08-14
  3. P3589R3C++ Profiles: The FrameworkGabriel Dos Reis · 2026-07-21
  4. P3970R0Profiles and Safety: a call to actionDavid Vandevoorde, Jeff Garland, Paul E. McKenney, Roger Orr, Bjarne Stroustrup, Michael Wong · 2026-02-23
  5. P4222R2An initialization profileBjarne Stroustrup · 2026-08-14
  6. P4238R0Returning C++26 for the Evaluation It SkippedVinnie Falco, Ville Voutilainen, José Daniel García Sánchez, John Spicer · 2026-08-14
  7. P4296R0Default-Deny + Provable-Whitelist InvalidationGuy Davidson, Sherry Ignatchenko, Dmytro Ivanchykhin, Marcos Bracco · 2026-07-14
  8. P4297R1Severing P3100’s Profiles Claim from Its Case-by-Case ReviewVinnie Falco, Ville Voutilainen · 2026-08-14
  9. P4306R1Configuring Runtime Checking: Profiles and Implicit Contract AssertionsVinnie Falco, Ville Voutilainen · 2026-08-14
  10. P4308R1Eight Responses to a Throwing Implicit Contract AssertionVinnie Falco, Ville Voutilainen · 2026-08-14
  11. P4318R1Transient Benefit, Perpetual Cost: Implicit Core-Language AssertionsVinnie Falco · 2026-08-14
  12. BlogC++ safety, in contextHerb Sutter · 2024-03-11