r/wg21
P3874R1 - Should C++ be a memory-safe language? EWG
Posted by u/unsafe_at_any_speed · 8 hr. ago

Document: P3874R1

Authors: Jon Bauman, Timur Doumler, Nevin Liber, Ryan McDougall, Pablo Halpern, Jeff Garland, Jonathan Müller

Date: 2026-02-23

Audience: EWG

This is the paper that asks the uncomfortable question out loud: should C++ formally commit to becoming a memory-safe language? Not “safer” — safe, with a definition. The authors propose that a memory-safe language must contain a syntactically explicit, compiler-enforced subset that is systematically free of all undefined behavior. Not reduced-by-profiles, not hardened-at-runtime — systematically free, as in: if you’re in the safe subset, you cannot trigger UB without an explicit unsafe escape hatch.

The Rust comparison is right there in the paper. Sean Baxter’s Circle compiler is cited as the existence proof that this architecture is implementable for C++ — safe/unsafe regions, borrow checking, lifetime annotations. Lifetime safety is identified as the hardest remaining problem. The paper explicitly says profiles (P3081) and contracts (P2900) don’t meet the bar — they reduce UB incidence but cannot eliminate it systematically.

This is not a proposal for a specific feature. It is asking EWG whether C++ should commit to this direction at all. Which means the real audience is everyone who has an opinion about what C++ should become — which on r/cpp is everyone.

▲ 412 points (87% upvoted) · 48 comments
sorted by: best
u/committee_gonna_committee 387 points 7 hr. ago

committee gonna committee, but at least this time they are committing to the direction of committing to maybe doing something about the thing that everyone else solved five years ago

u/rust_is_the_answer_bro 156 points 7 hr. ago

they already solved it, it’s called Rust

u/linker_error_survivor 89 points 6 hr. ago

thanks, you are the 400th person to say this in a WG21 paper thread today

u/daily_undefined_behavior 203 points 5 hr. ago

in fairness, the 400th time isn’t wrong either

u/linker_error_survivor 47 points 4 hr. ago

🫱

u/firmware_archaeologist 218 points 6 hr. ago

The key question this paper does not answer: what percentage of real C++ can actually live in the safe subset without unsafe annotations?

For the code I work with daily — embedded, bare-metal Cortex-M — the answer is not encouraging. Things that need unsafe in any reasonable model:

  • DMA buffer management (raw pointer arithmetic into memory-mapped regions)
  • MMIO register access
  • Interrupt service routines (they break borrow semantics by definition — the interrupt fires at any point and touches whatever it wants)
  • Any use of volatile with hardware-facing memory
  • Manual ring buffers with explicit lifetime management
  • Linker-script-defined stack/heap boundaries accessed via raw symbol addresses

For a typical firmware image I ship, maybe 6–10% of the code could plausibly live in a safe subset. The paper uses Rust as the existence proof, but Rust’s safe/unsafe ratio only looks good because Rust has a decade of safe abstractions: Vec, Arc, Mutex, Pin. C++ has std::vector, which itself would need to be redesigned to expose a safe interface under borrow checking — and std::mutex, which makes no lifetime promises a borrow checker could verify.

This is the gap between “the model is sound” and “the model is useful.” Circle proves the borrow-checker model can work for C++. It does not prove that 80% of production C++ could be in the safe zone.

I am not saying don’t do this. I am saying the paper needs to engage with the coverage question or it is not telling us anything we can act on.

u/constexpr_everything_irl 94 points 6 hr. ago

this is the comment. the rest of the thread is noise.

u/memory_safe_or_bust_cpp 71 points 5 hr. ago

The Rust answer to this is worth engaging with: safe Rust is not supposed to replace all unsafe C++ patterns immediately. The model is that you encapsulate the unsafe invariants behind safe abstractions. Vec is unsafe internally and safe externally — that’s the whole point.

The embedded situation is genuinely harder because you often have no clean boundary between “safe logic” and “hardware interface” — they are interwoven. But Rust has made real inroads in embedded precisely by building safe HAL layers (embedded-hal, cortex-m) that encapsulate the unsafe register access behind sound APIs. A C++ equivalent would require the same investment.

The question is not “what percentage of today’s C++ is already safe”. It is “what percentage could be safe if we built standard library primitives that expose sound interfaces.” That is a very different number.

u/just_a_compiler_guy 43 points 5 hr. ago

I work on a compiler. I want to believe the “build safe abstractions over unsafe internals” argument, but there is a structural problem: C++ has 45 years of libraries with no soundness discipline. Every std:: type that needs to expose a safe interface requires audit and likely redesign. std::span is a great example — it is trivially constructable with a dangling pointer right now. A borrow-checked std::span is a different API with different construction rules.

I ran Circle on a moderately complex codebase — standard library containers, some templates, a few custom allocators. About 40% of the code needed unsafe annotations or failed to compile in safe mode entirely. Some of that is fixable with better library design. A lot of it is fundamental: you cannot make placement new safe, you cannot make reinterpret_cast safe, and they appear in more places than you would expect.

The paper is asking EWG to commit to a direction. Fine. But directions cost nothing. The real commitment is in the subsequent papers that say “here is what safe std::vector looks like, and here is what your existing code that calls vec.data() now needs to do.” Those papers are going to be brutal.

u/memory_safe_or_bust_cpp 31 points 4 hr. ago

A borrow-checked std::span is a different API with different construction rules.

Yes, and that is fine? The whole premise of the paper is that there will be a new safe subset with new APIs. Nobody said std::span gets magically retrofitted. The migration story is: new safe APIs coexist with old ones, code in the safe subset uses the safe APIs, unsafe code uses the old ones.

The 40% number from your Circle experiment is interesting — do you know if that’s 40% of lines or 40% of translation units? If it’s 40% of lines but 5% of TUs, all clustered in allocator and container internals, that is actually a reasonable outcome. The unsafe surface is isolated and auditable.

The paper explicitly does not ask anyone to convert existing code. It is asking EWG to commit to developing language features that make a safe subset viable. The existing code stays unsafe by default. That is the whole point of the superset architecture.

u/just_a_compiler_guy 24 points 3 hr. ago

Fair point on TU concentration — I did not measure that carefully. You are probably right that the unsafe surface concentrates in container/allocator code if you do it properly.

My real concern is timeline and fragmentation. We are talking about a safe language variant shipping in C++35 at the absolute earliest, ergonomic parity with Rust maybe C++38. During that window, every C++ shop that needs to answer the “is your code memory-safe” question on a government contract has to pick between: Rust now, profiles-plus-hardening now (imperfect but available), or wait a decade for the C++ safe subset.

A direction commitment creates a third option that is theoretically superior but not available for ten years. I am worried that paradoxically accelerates Rust adoption — C++ says “we’re working on it,” procurement officers pick Rust because “working on it” does not satisfy a compliance checkbox.

u/memory_safe_or_bust_cpp 18 points 2 hr. ago

That is actually the best argument against this paper and you buried it in turn four.

I do not have a clean rebuttal. If the direction commitment delays real-world adoption of safe C++ by a decade while positioning C++ as “we’re working on it,” that is a genuine strategic error. You want C++ to either be safe now via profiles, or admit it cannot be and fund migration.

I still think the paper is right that profiles are not systematically safe. But “systematically safe in 2035” may be worse than “heuristically safe in 2026.”

Editing my earlier position: the direction commitment makes sense if the committee also accelerates profiles work as a short-term bridge. If the commitment replaces profiles investment, I agree with you.

u/rust_whisperer_2023 -47 points 7 hr. ago

just use Rust lol

u/autosar_stack_maintainer 143 points 7 hr. ago

yes, I will simply rewrite the AUTOSAR stack

u/avionics_throwaway 87 points 6 hr. ago

and the DO-178C certified flight software

u/safety_cert_survivor 52 points 6 hr. ago

and convince the certification authorities that Rust’s MISRA compliance story is settled (it is not, as of the time of this writing)

u/actually_reads_papers 156 points 5 hr. ago

Profiles can meaningfully reduce the number of UB-triggering operations in a program, but they cannot eliminate them; coverage is inherently incomplete.

This is the sentence the whole paper turns on. The word “systematically” in the proposed definition is doing enormous work. If “systematic” means “a formal proof guarantee,” then profiles fail on principle even if they achieve 99.9% coverage. If it means “practical elimination of the dangerous cases,” then profiles might succeed.

The paper is right that there is a categorical difference between the two. It is less right that the categorical difference matters for every threat model. For a government contractor asking “are you using a memory-safe language,” the categorical distinction is everything — checkbox compliance. For a security researcher asking “can an attacker exploit this binary,” what matters is whether the exploitable paths are closed, not whether the language model is formally clean.

The paper is arguing for the checkbox. That is a legitimate position. But it should say so explicitly rather than framing profiles as categorically insufficient for safety when they may be practically sufficient for security. Those are not the same word.

u/move_semantics_are_fine 38 points 5 hr. ago

saving this comment for the next time someone asks me why profiles aren’t enough

u/former_herb_hater_reformed 67 points 4 hr. ago

The safety vs. security distinction is doing real work here and the paper elides it. The CISA/NSA mandate language is about security (exploitability), but the paper’s definition is about safety (formal UB freedom). They are not the same bar.

A language where profiles eliminate 99.8% of exploitable memory bugs is arguably sufficient for the government mandate. A language where safe code can still trigger UB on the 0.2% edge case fails the paper’s formal definition. Whether that gap matters depends on whether you are trying to satisfy a compliance checkbox or trying to enable formal verification.

u/template_wizard_2019 28 points 3 hr. ago

wait, the paper actually addresses the safety/security distinction? I only read the abstract and skimmed the motivation section, like god intended

u/actually_reads_papers 19 points 2 hr. ago

It does not make the distinction explicitly — that is the critique. The paper uses “safety” and “security” somewhat interchangeably when citing government mandates, then pivots to a formal language-theoretic definition of safety. The conflation is doing work in the argument.

u/government_mandate_survivor 94 points 6 hr. ago

The NSA, CISA, ONCD, and various European cybersecurity agencies all issued guidance telling us to use memory-safe languages. C++’s response is a direction paper asking if it should try to become one. The Rust Foundation responded by forming a safety-critical consortium and landing Rust in the Linux kernel. I am sure this is fine.

u/process_cynic_42 67 points 5 hr. ago

to be fair, asking “should we do this” before doing it is an improvement over previous WG21 practice of doing it for 15 years and then asking

u/networking_paper_watcher_2019 43 points 4 hr. ago

this is the most accurate description of the networking/executors/coroutines situation I have ever read

u/circle_lang_enjoyer 89 points 5 hr. ago

I have been using Circle (Sean Baxter’s safe C++ superset) seriously for about eight months. The borrow checker works. It catches use-after-free and dangling references at compile time that Valgrind finds at runtime. The ergonomics differ from Rust’s borrow checker in interesting ways — Circle is more permissive on some patterns Rust rejects conservatively, stricter on others.

The paper cites Circle as the existence proof. Fair. What the paper cannot answer is whether Clang, GCC, and MSVC will implement the same borrow-checking model. Sean Baxter is one person. The C++ compiler infrastructure is three independent codebases with competing ABI constraints and different organizations with different priorities.

“Circle does it” is necessary but not sufficient as a feasibility argument. The paper needs an implementation commitment from at least one major vendor, not just a prototype fork.

u/just_a_compiler_guy 34 points 4 hr. ago

This. The paper’s feasibility argument would be substantially stronger with even one sentence from a Clang or GCC maintainer saying “we believe this is implementable in our codebase.” Without that, “Circle proves it” is closer to “one person’s graduate project proves it,” which is a different claim.

u/STL_Moderator 1 point 8 hr. agoModerator

Reminder: the paper authors and WG21 members read these threads. Technical disagreements are welcome. “Just use Rust” as your complete argument is not. Keep it civil.

u/borrow_nothing_cpp 52 points 4 hr. ago

The irony the paper skips: Rust’s borrow checker itself has known limitations that active research is still trying to fix. The Polonius project (next-generation borrow checker) has been “almost ready” since roughly 2018. Group borrowing is an open research problem. Non-lexical lifetime edge cases still reject valid programs. C++ is proposing to standardize on a model that Rust is still iterating on.

None of that is disqualifying — you pick the best available model and iterate. But the paper presents the Rust model as settled, which it is not.

u/coroutine_hater_2000 34 points 4 hr. ago

Polonius has been “almost ready” since 2018, so C++ borrow checking is in good company

u/definitely_not_bjarne -23 points 3 hr. ago

[removed by moderator]

u/linker_error_survivor 29 points 3 hr. ago

what did they say?

u/actually_reads_papers 41 points 2 hr. ago

something about Rust developers not understanding systems programming. the usual.

u/compiles_eventually 76 points 3 hr. ago

“new language features requiring per-function borrow analysis”

laughs in 45-minute build times

u/build_system_victim_2024 44 points 2 hr. ago

borrow checking is actually a dataflow pass, it’s not particularly expensive compared to template instantiation

u/compiles_eventually 31 points 2 hr. ago

tell that to my seven-level recursive template instantiation that is currently borrow-checking itself into the void

u/UB_enjoyer_69 23 points 2 hr. ago

if we eliminate all UB then half my job as a compiler optimizer disappears. I am contractually obligated to oppose this paper.

u/constexpr_everything_irl 56 points 2 hr. ago

the only genuine conflict of interest disclosed in this thread

u/networking_ts_still_waiting 138 points 2 hr. ago

can we please get networking in the standard before we redesign the entire safety model

u/executor_enjoyer_420 62 points 1 hr. ago

executors have entered the chat

u/[deleted] -5 points 1 hr. ago

[deleted]

u/just_a_cpp_dev_throwaway 8 points 1 hr. ago

wait does this mean my existing code stops compiling? I have 600kloc of C++11 that I cannot touch for contractual reasons

u/actually_reads_papers 44 points 1 hr. ago

No. The paper explicitly proposes a superset model: existing C++ is unsafe by default and compiles unchanged. New safe regions are opt-in. Your 600kloc is fine; it simply cannot make formal safety guarantees. This is the whole point of the superset architecture — Circle already validates that existing C++ code compiles as-is under the model, it just sits in the unsafe zone.

u/abi_archaeologist 27 points 47 min. ago

“compiles unchanged” assumes no ABI implications from whatever the safe-subset annotations require, which is optimistic given WG21’s track record with ABI — but yes the design goal is backward compatibility

u/firmware_archaeologist 63 points 1 hr. ago

One thing I want to add to my earlier comment: I re-read section 4 on the phased strategy and the paper is more honest about the long timeline than I initially gave it credit for. It explicitly calls out lifetime safety as requiring “significant new language features” and does not pretend that C++26 or even C++29 addresses it. The ask is genuinely just “should EWG commit to the direction” — not “sign off on a feature.”

That changes my read a bit. I can support a direction commitment as long as profiles work (P3081) continues in parallel as the near-term bridge. What I do not want is for this direction paper to be used as a reason to deprioritize profiles on the grounds that “we’re doing the real thing instead.”

u/not_a_committee_member_wink 112 points 3 hr. ago

The tell that this paper is serious is the author list. Timur Doumler, Nevin Liber, Pablo Halpern — these are not people who write directional papers on a whim. When that group puts their names on “should we do X,” the implicit message is “we have already thought hard about whether X is achievable and we believe it is.”

The paper is not the proposal. The paper is the canary poll. EWG answers yes, the proposals follow. Worth reading the author list before dismissing it as wishful thinking.

u/process_cynic_42 44 points 2 hr. ago

the implicit message is also “we are willing to do the work,” which matters even more than the belief that it’s achievable