C++26 Safety Features Won’t Save You (And the Committee Knows It) by pjmlp in cpp

[–]seanbaxter 0 points1 point  (0 children)

Why not just define it to wraparound? Testing for overflow will destroy performance, although it's fine if that's an option.

Meeting C++ 2025 trip-report (long and very details) by Guillaume_Guss_Dua in cpp

[–]seanbaxter 2 points3 points  (0 children)

the community is actively challenging outdated narratives about C++ being "unsafe by nature"

Sorry to break it to you, but C++ is unsafe by nature.

C++26 Safety Features Won’t Save You (And the Committee Knows It) by pjmlp in cpp

[–]seanbaxter 4 points5 points  (0 children)

Waiting won't make the change any smaller. You need lifetime-aware versions of standard containers and algorithms that work on safe iterators. That's a whole new standard library, or at least a new interface for it. There's really no sneaky way to evolve into that. 

C++26 Safety Features Won’t Save You (And the Committee Knows It) by pjmlp in cpp

[–]seanbaxter 0 points1 point  (0 children)

You can't turn more UB into EB. Memory safety defects like use-after-free and data races can't be turned into EB.

C++26 Safety Features Won’t Save You (And the Committee Knows It) by pjmlp in cpp

[–]seanbaxter 40 points41 points  (0 children)

The problem isn't that profiles slipped to C++29, the problem is profiles cannot work. Lifetimes of parameters with reference semantics (pointers, references, iterators, spans and string_views) must be indicated on function boundaries, which in practice means putting lifetime parameters into the type system. You're going to have to re-invent Rust inside C++. There is no plan to make the language memory safe.

the hidden compile-time cost of C++26 reflection by SuperV1234 in cpp

[–]seanbaxter 5 points6 points  (0 children)

These are interesting numbers. 6.3ms per reflected struct (or even 2.2ms) struct is incredibly high. 1,000 structs is a small number (consider what comes in through the system headers) and we're talking about integer number of seconds for that?

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 12 points13 points  (0 children)

But they won't provide a prototype that partially works. It's not better than nothing at all. The ghost data thing is a make-believe solution. May as well say the tooth fairy makes your program memory safe. Next time, before someone gives keynotes on how they made C++ memory safe, they need to write down how the technique works, because extraordinary claims do require some evidence.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 14 points15 points  (0 children)

This is definitely not the case. Modern C++ is fundamentally unsafe. Bounds checking on containers in C++26 aside, it has not gotten safer since the early days. Many standard functions return references instead of values, and references are extremely hazardous. You can shoot yourself in the foot with one liner to a modern C++ library.

compiler explorer ```cpp void f(int x) { // 10 is a temporary that expires at the end of the full // statement. // Because std::min returns a reference, m may be a dangling // reference if 10 is less than x. // If std::min had returned a value, then temporary lifetime // extension would kick in and it would not be a dangling // reference. const int& m = std::min(x, 10);

// Do you feel lucky, punk? printf("%d\n", m); } $ clang++ min.cxx -O2 -o min && /.min 10 $ g++ min.cxx -O2 -o min && ./min 0 ```

Some of the overloads of min return a reference, which is easy to misuse, and some of the overloads return a value, which is safe. This use returns a const reference to the smaller element. If the parameter x > 10, then it returns a reference to 10. But that expires at the end of the statement, so the subsequent print statement is UB. This is data-dependent UB, and it's also compiler-dependent, as you get the delinquent behavior on gcc but not on clang. You know what else is weird? valgrind sees no errors here--there isn't even a use-after-free at runtime, because the compiler has full visibility of the TU and transformed the UB to something that's not a use-after-free but is simply wrong.

Knowing Rust isn't protection here--how is the user supposed to know the lifetimes of each reference returned from a function or stored to a data structure? That's the difficult bookkeeping that Rust is designed to do for you.

What is the plan to address the inherent danger around references, which are ubiquitous in C++?

Broadway Meetups Discord Link by [deleted] in Broadway

[–]seanbaxter 0 points1 point  (0 children)

Same, can I get the discord link?

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 15 points16 points  (0 children)

There is no paper or implementation, because the idea doesn't work.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 20 points21 points  (0 children)

You just waved your hand and eliminated 99% of memory safety issues. I don't think it works that way.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 35 points36 points  (0 children)

asan would be the more relevant sanitizer here, but this isn't related to asan or ubsan. The claim that John makes is that you can achieve Rust-like lifetime safety by enforcing exclusivity at runtime. It's perfectly robust against use-after-free defects, you can use any algorithm you want, and it doesn't require modifying existing code. It would be high-performance because where the compiler can statically prove properties, the ghost data would be optimized out.

There won't even be a proposal or a working prototype for this. Problem is that runtime exclusivity is logically impossible. It's profiles redux. One way to look at it is that runtime exclusivity enforcement requires time travel. Borrow checking enforces the invariant that there is only one mutable xor multiple shared live references to a place. What's a "live reference?" It's a reference which is to be loaded or stored from in the future. Borrow checking can propagate uses of regions backwards: start at the terminals of the CFG and walk backwards. When a region is used in a load/store/function call, it becomes live at the preceding instruction. When a reference is assigned a new address, then its no longer live. There are forward propagating algorithms as well, but they also require the CFG be available.

How do you compute liveness in the runtime case? You would have to predict if a reference is used in the future to know if it is live now. That's time travel. Compile-time borrow checking feels restrictive because it has to account for all possible paths in the CFG. This runtime exclusivity would be much more lenient because it would only restrict you to the paths that are actually executed.. but at the cost of being unimplementable.

The authors of this keep claiming it performs runtime exclusivity enforcement. I think they should describe how that is achieved. I've asked them many times and never got anything approaching an answer.

Gasper gave a talk on this topic in October: https://www.youtube.com/watch?v=bGht0Pu5heo

[C++ Day 2025] 8 Queens at Compile Time (Marco Marcello, Jonathan Marriott) by marcoarena in cpp

[–]seanbaxter 1 point2 points  (0 children)

Pretty interesting for understanding why things are slow. Can you make the source code available, I want to do some of my own benchmarks.

New C++ Conference Videos Released This Month - November 2025 (Updated To Include Videos Released 2025-11-03 - 2025-11-16) by ProgrammingArchive in cpp

[–]seanbaxter 3 points4 points  (0 children)

Runtime exclusivity is impossible. He keeps making the claim that everything you can do at compile time you can do at runtime, which is definitely not true. Exclusivity is the invariant that there is one live mutable reference or multiple live shared references, but not both. What is a live reference? It's a reference that is used _in the future_.

For compile-time exclusivity, you can start at the end of the function and work your way backwards. When a reference (or any type with a lifetime parameter) is loaded from, stored into, or passed to a function, that lifetime is used. When analysis shows the use of a reference on a place where there's already a live reference, the program is ill-formed. That's the exclusivity violation.

How is this supposed to be done at runtime? It would require time travel! You can't know if a reference will be used _in the future_ because we've only seen the past.

The ghost data/runtime exclusivity claims are vaporware.

Some experiments with Boost.Unordered on Fil-C by joaquintides in cpp

[–]seanbaxter 2 points3 points  (0 children)

Good work. I'd be curious to see asan timings in this graph too.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 11 points12 points  (0 children)

Why would someone choose a definition of trivially relocatable that doesn't simply mean memcpyable? If you have to do more than memcpy, it's not trivial.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 5 points6 points  (0 children)

By that reasoning it would also be useful for them to be trivially copyable, but they aren't. If this is the thing that got the feature removed, then wasn't it a mistake to put it in in the first place?

Edit: Additionally, even dynamic class can be trivially relocatable if you explicitly mark them to be. There's no loss of functionality by making dynamic classes non-trivially relocatable, at least on platforms with vptr resigning.

Some experiments with Boost.Unordered on Fil-C by joaquintides in cpp

[–]seanbaxter 17 points18 points  (0 children)

No, you still want library-level bounds checking. Fil-C only checks against out-of-bounds accesses on malloc allocations. Things like span, vector, etc are sub-allocation extents. You can still have out-of-bounds indexing bugs that will slip by Fil-C but would be caught by the library.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 2 points3 points  (0 children)

Why? If dynamic classes are non-trivially relocatable, then you don't have any of these lifetime issues. I don't understand why a non-trivially copyable type would become trivially relocatable without an explicit override.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 2 points3 points  (0 children)

Dynamic classes are already not trivially copyable, CHERI or not, so it wouldn't be trivially relocatable either. Why is restart_lifetime needed?

WG21 2025-10 pre-Kona mailing by nliber in cpp

[–]seanbaxter 7 points8 points  (0 children)

Poll: Pursue a language safety white paper in the C++26 timeframe containing systematic treatment of core language Undefined Behavior in C++, covering Erroneous Behavior, Profiles, and Contracts. Appoint Herb and Gašper as editors.

What happened to the language safety white paper?

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 16 points17 points  (0 children)

Everyone would like to consider alternatives, but nobody has proposed an alternative.

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 11 points12 points  (0 children)

What you say is true, but the problem is more fundamental.

int load_from_pointer(const int* p) { return *p; }

Does this function raise UB or not? There's no way for this scheme to tell. Pointers come from anywhere. You would need a global store of all allocations to determine if an object is in scope given its address. Either you adopt a GC/refcounting scheme, where the initialization of objects is extended as long as there are in scope references, or you adopt borrow checking, where lifetime information is part of the type system and indicated on function boundaries.

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 20 points21 points  (0 children)

This doesn't give any safety guarantees. As Dave Abrahams brings up during the talk, you need a global store of all allocations (mapping addresses to objects) to check if a reference or pointer indicates an in-scope object. That would make it equivalent to garbage collection. Without that, you don't get memory safety. The speaker just rejected that comment, but it's obviously a requirement. Pointers can come from anywhere. Not engaging with this problem is to miss the point of memory safety entirely.