C++ Logo

std-proposals

Advanced search

Re: [std-proposals] The Lakos rule, undefined behaviour and noexcept

From: David Brown <david.brown_at_[hidden]>
Date: Fri, 25 Sep 2026 18:19:54 +0200
On 25/09/2026 17:09, Frederick Virchanza Gotham via Std-Proposals wrote:
> In order to try get my head around all this "Lakos rule" stuff and to
> try enable in-depth discussion about it, I've added a new ABI to the
> GNU compiler: -m64nullex
>
> The 'm64nullex' ABI is the same as 'm64', but with three changes.
> Normally in C++, the following will just result in undefined
> behaviour:
> - Dereferencing a nullptr

You can't "dereference a nullptr" - "nullptr" is a specific instance of
type "std::nullptr_t" that can be converted to a null pointer value.
You can attempt to dereference a null pointer. Any attempt to
dereference a pointer that does not point to an object of the
appropriate type, is UB - and a null pointer is guaranteed not to
compare equal to a pointer to an object.

> - Performing arithmetic on a nullptr

You can add 0 to a null pointer value - that is not UB. Otherwise for a
pointer "p" and an integer "i", "p + i" is UB unless "p" points to an
item within an array of objects of suitable type and "p + i" points
either to another element in the same array, or one past the end of the
array. A single object can be views as an array of length 1 for this
purpose.

> - Passing a nullptr to a Standard C library function

There are some standard C library functions that are fine with null
pointer values. I can't see the details as important here, it just
seems a very strange claim to make.

>
> Let's have a quick little talk about what undefined behaviour really
> is. I remember a few decades ago, someone came out with the line "make
> demons fly out of your nose" -- their aim being to convey that the
> program can do anything at all. So to give an example, let's say that
> you have a program with 4 threads, and let's say in one of the
> threads, you simply do:
>
> int *p = nullptr;
> ++p;
>

Yes, that is UB.

And after you execute something with no defined behaviour, there are no
restrictions from the standards on what can happen.

For example, the compiler could support run-time diagnostics that catch
this and halt the program with a message - killing the other threads
outright.

> Even though you've only done this in one thread, and even though you
> haven't done any invalid memory accesses (no reads, no writes), the
> other 3 threads can now just do whatever they want... so if one of
> those other threads was just about to do "format d:", well it's now
> within its rights to do "format c:" instead.
>

Yes. It is unlikely in this case, but possible. In particular, the UB
you get from writing to an invalid pointer could perhaps write to the
memory where the string "format d:" resided and change it to "format
c:". Trying to specify what may or may not happen when something is
unpredictable is folly - thus UB does not attempt to do so.

> So this now begs the question,

(No, it /raises/ the question. But that's just my never-ending campaign
for more accurate use of idioms, rather than a C++ matter!)

> and please pay particular attention to this:
> "Where the ISO C++ Standard says that a particular thing results
> in undefined behaviour, is it okay for a particular compiler to say
> that the behaviour is well-defined and that the entire program must
> continue to function as normal?"

It does not, IMHO, make sense to say "results in undefined behaviour" -
though the C++ standard certainly uses that phrasing. UB is not
something you can have as a result - it is a lack of definition for the
behaviour.

An implementation can certainly give semantics to something that the C++
standard leaves undefined. It can document these semantics (in which
case it is fine to rely on them, though the program will not be
portable), or it can consistently handle them in some undocumented way
(then relying on them is dangerous, but possible), or it can just happen
to generate consistent code as part of its normal code generation.

It can also conditionally implement particular semantics. So "gcc
-fwrapv" gives two's complement wrapping semantics to signed integer
overflow - without that, it is UB and the compiler can optimise on the
assumption that no signed integer overflow occurs.

>
> On the 'm64nullex' ABI, if you do something wrong with a nullptr, it
> will result in the throwing of an exception. And let me make a point
> here: "The throwing of an exception is an acceptable form of undefined
> behaviour".

Again, it makes no sense to talk about about "doing something wrong with
nullptr". The only thing you can do with nullptr is convert it to a
null pointer value of pointer type - and that is always safe and fully
defined.

There are several things you can "do wrong" with a null pointer value,
in that the expressions are UB. An implementation can throw an
exception if it wants - whether it documents it or not. (I think that
is unlikely to be a good idea, but that's just opinion, not a
restriction of what is allowed.)

>
> But now there's the sticky part: "Just because the throwing of an
> exception is an acceptable form of undefined behaviour, does this mean
> that the entire program must continue to function as normal?"

That makes no sense to me.

 int * p = nullptr;
 ++p;

is UB.

A compiler can do whatever it wants here. It can (documented or not)
treat the code is as though you had written :

 throw null_ptr_aritmetic();

As far as the rest of the program is concerned, it's just a throw.

The compiler can also (documented or not) kill the other threads and
then throw. Or it can, if it can see that the UB is inevitable, kill
the program before the other threads are started. Or run them three
times instead.

There are no restrictions on what happens with UB - no requirements at
all. But if an implementation decides to pick a method of implementing
something that is UB in the C++ standards, and documents that method,
then it should stick to what it has documented. And it is good practice
to make that documentation clear and unsurprising.

>
> Here are the exceptions I've added to the C++ Standard Library:
>

No, they are not added to the C++ standard library. They are perhaps
added to a header you have.

> namespace std {
> struct nullptr_error : exception { ... };
> struct nullptr_dereference : nullptr_error { ... };
> struct nullptr_arithmetic : nullptr_error { ... };
> struct nullptr_argument : nullptr_error { ... };
> }
>
> Therefore if you compile and run the following:
>
> int main()
> {
> int *p = nullptr;
> return *p;
> }

Note that attempting to dereference a null pointer value is different
from arithmetic on a null pointer value. And it is something that is
already diagnosed in many systems. As it stands on Linux, the program
above will give a segmentation fault. With the "-fsanitize=undefined"
flag, you will get a nice clear runtime error message that includes
location details.

You have not been at all clear if you are talking solely about pointers
derived from a conversion from "nullptr", or if you are talking about
pointers that happen to have the value 0.

> Okay so now that we have this cool new compiler, let's play around
> with it. Let's write an efficient function to copy a string:
>
> void StrCopy(char *p, char const *q)
> {
> while ( *p++ = *q++ );
> }
>
> Let's document this function to say that you shouldn't pass nullptr's
> to this function, and that the behaviour is undefined if you do give
> it nullptr's.
>
> Now now now now now now............... what to do here. Should we
> stick 'noexcept' on our StrCopy? Let's consider a few things.
> (1) - If we do not mark this function as 'noexcept', and if we give
> it a nullptr, it will throw std::nullptr_error. Nothing too
> controversial here.
> (2) - If we mark this function as 'noexcept', and if we give it a
> nullptr, then what should happen?


In your new C++ language variant where dereferencing a null pointer
value has throw semantics, the function should not be marked "noexcept"
- instead you must say "noexcept(p && q)".

This quickly becomes silly and excessive.

>
> You can see that 'std::terminate' has been called. Let me suggest a
> crazy half-baked idea here though... what if 'nullptr_error' were to
> be given special status among the exception types?

Are you /seriously/ suggesting that there should be a special
inconsistent handling of "noexcept" just for this one exception type
that is not going to be used by anyone in real code, and for which there
are already neater and more efficient run-time debug solutions?

>
> I'm just throwing out ideas here to get discussion going.

Do more thinking, and less "playing around".

And next time, if you don't understand basic terms of the C++ standards,
either read about them or ask in an appropriate place (Stack Overflow,
comp.lang.c++, etc.) rather than wasting your own time making silly
compiler forks or posting nonsense to the C++ standards proposals list.
This is not a list for "throwing out crazy half-baked ideas".

Received on 2026-09-25 16:20:03