4

This code compiles in GCC 8, but not in GCC 7 nor clang.

constexpr int a = 1;
constexpr int b = --const_cast<int&>(a);

This is clearly UB.

My question: what does standardese say about evaluating a constexpr that contains UB - should this code compile at all?

PoweredByRice
  • 2,479
  • 1
  • 20
  • 26
  • 3
    It is not immediately obvious to me why this is UB? Can you quote applicable Standard section? – SergeyA Sep 12 '19 at 15:16
  • 1
    @SergeyA That's what the question is asking. OP is asking which part of the standard says this is UB. – François Andrieux Sep 12 '19 at 15:19
  • 5
    @FrançoisAndrieux this is not how I read it. OP's saying: *"This is clearly UB."* To me this is not a question, but a statement by OP. – SergeyA Sep 12 '19 at 15:20
  • 2
    @SergeyA You can't modify a const object but that is exactly what `--const_cast(a)` does. – NathanOliver Sep 12 '19 at 15:21
  • 1
    @SergeyA But if you read the following sentence *" what does standardese say about this?"* it's clear OP is asking for the same reference you asked. The first part expresses that the code is obviously flawed. Maybe the use of UB is not exact (because it begs the question), but the question otherwise seems clear. – François Andrieux Sep 12 '19 at 15:21
  • 1
    What does “this” refer to? Modifying a `constexpr` or something else? – molbdnilo Sep 12 '19 at 15:22
  • 2
    As far as I know `constexpr` implies `const`, and thus you are modifying a `const` reference, and that is undefined behavior. See https://stackoverflow.com/questions/25209838/is-this-undefined-behavior-with-const-cast – pqnet Sep 12 '19 at 15:24
  • I realized now that the question is not about undefined behavior, but about typing, that is _should this compile or not_? If so, could you clarify the question? – pqnet Sep 12 '19 at 15:28
  • `clang++` is happy with this though: `constexpr int a = 1; int b = --const_cast(a);` - but segfaults directly when starting the program. Perhaps `g++` creates a temporary in the cast? `a` is not modified when compiled with `g++`. – Ted Lyngmo Sep 12 '19 at 16:02

2 Answers2

5

GCC 8 is wrong

All undefined behavior in a constexpr compile-time expression makes the expression not evaluated at compile time (not consteval basically, to use a new keyword that basically has that meaning).

Initializing a constexpr requires, effectively, a consteval expression.

I won't address if what you did is UB (I believe it is), but if it is UB then it should not compile.

I'll see if I can find standard quotes to back these assertions; but there is no tricky ing required here. Just a simple principle: At compile time during evaluation of compile time expressions, compilers must audit the code it runs for UB, and if they run into UB (again, at compile time) the expression isn't a compile-time expression anymore.

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • For what it's worth, VC++ complains with "error C2131: expression did not evaluate to a constant" about how `b` is initialized. – François Andrieux Sep 12 '19 at 15:27
  • Can a compiler refuse to accept code which causes undefined behavior? I think not. – pqnet Sep 12 '19 at 15:29
  • 1
    @pqnet The expression, if evaluated, contains UB, so it isn't a consteval, and you must initialize constexpr with consteval. There is no "UB" in the resulting program, just an ill-formed program. In effect, at compile time, the compiler *must* audit everything for UB, and cannot let you run UB at compile time. – Yakk - Adam Nevraumont Sep 12 '19 at 15:30
  • 3
    @pqnet It's mandated that there be no UB in a `constexpr` so in this case it has be rejected. – NathanOliver Sep 12 '19 at 15:33
  • So, the conclusion is that GCC shouldn't accept this code and emit an error, and therefore it is a bug in GCC 8 (and up), right? – Yksisarvinen Sep 12 '19 at 15:38
  • 1
    Yes, this code is ill-formed. Someone should file a bug against GCC 8. – Brian Bi Sep 12 '19 at 15:39
  • I wouldn't be surprised if the bug report exists already, but I can't find it. – Brian Bi Sep 12 '19 at 15:39
  • @pqnet Even for UB in general, why couldn't a compiler that is able to detect certain sources of UB fail to compile when it does? Isn't that what you do if you treat warnings as errors, to some extent? – François Andrieux Sep 12 '19 at 15:41
  • 1
    @FrançoisAndrieux If a C++ compiler can prove that all executions of a program cannot be prevented from resulting in UB, then it can replace the "output the program" with a paper flag (or whatever else it wants), including something that cannot be executed (like an error message printed to console). – Yakk - Adam Nevraumont Sep 12 '19 at 15:47
  • Although `consteval` isn't allowed on a variable declaration. (A bit confusing IMHO - why not just have `constexpr` and `consteval` mean exactly the same thing for a variable?) – aschepler Sep 12 '19 at 16:07
  • The normal meaning of "Undefined Behavior", as the term is used in the Standard, is "actions or constructs for which the Standard would impose no requirements"; implementations may or may not define the behavior of such constructs as they see fit. The use of a pre-increment operator should disqualify the expression from being a compile-time constant regardless of any UB, but a compile-time constraint that a piece of code be free of UB would contradict the *meaning* of Undefined Behavior unless it is interpreted as applying only to implementations that don't define the behavior. – supercat Sep 12 '19 at 23:52
3

This code is ill-formed, and GCC 8 and 9 are incorrect to not give a diagnostic.

[expr.const] (C++17 paragraph 2, current C++20 draft paragraph 4):

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • ...

  • an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard;

  • ...

  • modification of an object unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

  • ...

"Clauses [intro] through [cpp]" is also known as the core language specification.

[expr.const] (C++17 paragraph 5, current C++20 draft paragraph 10):

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

The "following constraints" apply only to values of class type, array type, or pointer type.

[dcl.constexpr] (C++17 paragraph 9, current C++20 draft paragraph 10):

In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

The expression --const_cast<int&>(a); is not a core constant expression and therefore not a constant expression, both because its evaluation would have undefined behavior, and because it modifies an object whose lifetime did not begin within the evaluation. A "shall" statement (unless combined with "no diagnostic required") means that an implementation must print a diagnostic message (e.g. an error or warning) when a program violates it.

Community
  • 1
  • 1
aschepler
  • 70,891
  • 9
  • 107
  • 161