Constexpr version of function gives different result when called the same way

Viewed 131

main.cpp:

#include <iostream>

static constexpr bool f1()
{
    auto p = new int(1);

    delete p;    

    auto q = new int(2);
    
    delete q;
            
    return p == q;
}

static bool f2() // Same body as f1
{
    auto p = new int(1);

    delete p;    

    auto q = new int(2);
    
    delete q;
            
    return p == q;
}

int main()
{
    constexpr bool i1 = f1();
    
    std::cout << i1 << std::endl;
    
    auto i2 = f2();
    
    std::cout << i2 << std::endl;
}

Compilation command line:

clang++ -std=c++20 -pedantic-errors main.cpp -o prog

Output from running prog (this is what I got, but may be different for you):

0
1

How is this possible? How is it even possible that I am allowed to define f1 that way given that it has unspecified behaviour?

1 Answers

Deleting a pointer invalidates it.

Any use of an invalid pointer value has implementation-defined behavior (except for indirecting through and passing to a deallocation function, which have undefined behaviour; neither is done in the example).

In the example that behaviour happened to be different in two slightly different cases.


How is this possible?

The compiler produced a program that outputs "0\n1". It is possible.

If you want to know if this conforms to the standard: Yes.


Whether this is intentional by the implementation... I suspect not directly, but rather by coincidence. My entirely hypothetical guess about the implementation:

There may be a piece of logic that sets invalid pointers to null. This has the useful side-effect that programs that have "use after free" bug (undefined behaviour) are less likely to read/write arbitrary memory (heap smashing) and instead avoid that due to null pointer check, or outright crash due to indirecting through null pointer. This potentially would reduce the severity of security vulnerabilities caused by such bug. As a side-effect, two unspecified values that would happen to be null pointers would also happen to compare equal.

But in constexpr case there may be another piece of logic which analyses that the pointers never point to same object and therefore are never equal, and constant-fold the return value as false before the null "protection" occurs.


Standard quote:

[basic.stc]

When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of that region of storage become invalid pointer values. Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.31

  1. Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault. ⮥
Related