Is reading a variable outside its lifetime during constant evaluation diagnosable?

Viewed 287

Shall one expect a reliable failure of in constant evaluation if it reads a variable outside of its lifetime?

For example:

constexpr bool g() {
    int * p = nullptr;
    {
        int c = 0;
        p = &c;
    }
    return *p == 0;
};

int main() {
    static_assert( g() );
}

Here Clang stops with the error

read of object outside its lifetime is not allowed in a constant expression

But GCC accepts the program silently (Demo).

Are both compilers within their rights, or GCC must fail the compilation as well?

2 Answers

GCC dropped the ball.

[expr.const]

5 An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • ...
  • an operation that would have undefined behavior as specified in [intro] through [cpp];
  • ...

Indirection via dangling pointer has undefined behavior.

[basic.stc.general]

4 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.

So the invocation of g() may not be a constant expression, and may not appear in the condition of a static_assert which must be constant evaluated.

The program is ill-formed.

The above quotes are from the C++20 standard draft, but C++17 has them too.

Shall one expect a reliable failure of in constant evaluation if it reads a variable outside of its lifetime?

Yes, but your example doesn't necessarily do this. Its behavior is implementation-defined.

When the block with the variable c exits ([basic.stc.auto]/1), the value of p becomes an invalid pointer value ([basic.stc.general]/4).

When *p is evaluated, the lvalue-to-rvalue conversion ([conv.lval]) is applied to p. And [conv.lval]/3 says:

The result of the conversion is determined according to the following rules:
...
— Otherwise, if the object to which the glvalue refers contains an invalid pointer value, the behavior is implementation-defined.

So.

Are both compilers within their rights, or GCC must fail the compilation as well?

AFAIK neither of the implementations define its behavior here, but I think it could theoretically be defined in such a way that neither the conversion nor the rest of the evaluation would make g() not a constant expression.

Related