5

I came across the following claim:

Actually, all forms of UB in the language are required to be caught when evaluating a constant expression (though UB in the standard library is not required to be caught). It's only runtime UB where anything can happen.

(emphasis mine)

My question is that is the above statement technically correct?

Upon asking the user how does the standard impose this, they cited expr.const#5.8, which states:

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:

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

But after reading the above [expr.const#5.8], I could not figure out how this implies that all forms of UB in the language are required to be caught when evaluating a constant expression. So can someone clarify how does this citation support (if it does) the claim made in the comment quoted above?


I also read this which says:

If the behavior is undefined, the compiler could accept it, reject it, issue a warning, and according to the standard, even crash, hang or install a virus on your computer.

So it seems to me (upon reading the very first comment) that there is a fundamental difference between UB during the evaluation of a constant expression and a runtime UB.

What is the truth?

Jason
  • 36,170
  • 5
  • 26
  • 60
  • 1
    _Is it necessary that all forms of Undefined Behavior are caught when evaluating a constant expression_ No. If there is a constexpr function that can never be invoked as a subexpression of a constant expression, all Standard requirements, including those to capture UB in constant expressions, are off. (https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr#6.sentence-1, https://timsong-cpp.github.io/cppwp/n4861/intro.compliance#2.3). This applies to all sources of IFNDR, of course. – Language Lawyer May 10 '22 at 09:01
  • 1
    I wouldn't interpret those clauses as meaning that the compiler must "catch" all forms of undefined behaviour. My intepretation would be that those clauses are saying that evaluation of a core constant expression cannot involve any operation with undefined behaviour. Which means, if the behaviour is undefined, that all bets are off .... two compilers may produce completely different outcomes, one may electrocute the programmer, and the other may yield a numeric value of 42, irrespective of what the programmer intended. – Peter May 10 '22 at 09:03
  • 1
    It isn't a `constexpr` unless it *doesn't* have UB. The comment you cited is completely incorrect. – user207421 May 10 '22 at 09:40
  • IIRC there are cases of UB, which can only be detected by solving the halting problem. So at least you (or the creators of the standard and the implementors of the compilers) cannot determine (in each case for each possible function), whether a function is free from UB for all possible inputs (Turing proved this). This does not mean there could not be also UB for input known at compile-time? – Sebastian May 10 '22 at 15:32
  • @Sebastian The compiler only needs to diagnose UB for the specific inputs given to the function, meaning that it can simply evaluate it. It doesn't need to determine whether the function has UB for any other input. The compiler is also allowed to set an upper limit on the number of instructions to evaluate before giving up and considering the expression not a constant expression. There is still [CWG issue 2192](https://www.open-std.org/Jtc1/sc22/wg21/docs/cwg_active.html#2192) which currently would require the compiler to explore multiple evaluation paths, which is unreasonable. – user17732522 May 10 '22 at 16:01
  • I see my linked comment was unclear. Yes, the compiler can only catch UB that's *invoked* as the result of constant evaluation. If UB *is* invoked as the result of constant evaluation, then yes, to my understanding that makes the program ill-formed, and a diagnostic required (unlike invoking UB at runtime, where no diagnostic is required). In the OP's example on the linked question, the result was assigned to a constexpr variable, which requires it to be constant evaluated, and I think a diagnostic is required if it invokes UB as a result of that evaluation. – cigien May 10 '22 at 19:04
  • Also, I think this might be a duplicate of https://stackoverflow.com/questions/21319413. The only salient difference appears to be the contents of my linked comment, which isn't particularly interesting. Apart from the fact that it's just a comment from some person, which doesn't carry much weight, I could simply be wrong). Not voting to close as a duplicate, since I'm *involved* in some sense. – cigien May 10 '22 at 19:06
  • Another possible dupe: [Why is using a reserved identifier name in constexpr context not diagnosed?](https://stackoverflow.com/questions/70244992/why-is-using-a-reserved-identifier-name-in-constexpr-context-not-diagnosed/70245047#70245047). – dfrib May 11 '22 at 13:24

1 Answers1

5

i could not figure out how this implies that all forms of UB in the language are required to be caught when evaluating a constant expression.

Not necessarily for all forms of UB. As per the quoted rule, only if operation is that is evaluated would have undefined behavior as specified in [intro] through [cpp];.

No other UB such as specified in other sections, or UB that isn't caused by evaluation of an operation, prevents an expression from being core constant. There is a clarifying rule:

[expr.const]

If E satisfies the constraints of a core constant expression, but evaluation of E would evaluate an operation that has undefined behavior as specified in [library] through [thread], or an invocation of the va_­start macro ([cstdarg.syn]), it is unspecified whether E is a core constant expression.

This clarification (including the "as specified in ..." sentence from question) is a resolution to defect report 1952 and the wording is in C++17.


To clarify, the rule causes certain UB to prevent an expression from being core constant. Consider a case where a rule requires an expression to be constant. Here is a an example of such rule:

[dcl.array]

 D1 [ constant-expression opt ] attribute-specifier-seq opt 

... The constant-expression shall be a converted constant expression of type std​::​size_­t ([expr.const]). Its value N specifies the array bound, i.e., the number of elements in the array; ...

If some context requires an expression to be constant, then the expression not being constant will violate that rule. In such case this applies:

[intro.compliance.general]

If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this document as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • My understanding of this is that(as given in your quoted statement) it is unspecified whether `E` is a core constant expression. But it does not mean that it must be caught. Does it? Or am i missing the point you're making? – Jason May 10 '22 at 09:10
  • @AnoopRana If it is a core constant expression, then using it in a constantly evaluated context doesn't make the program ill-formed. If a program is well-formed, then it shall compile. The compiler is free to detect the issue it and provide a diagnostic message. – eerorika May 10 '22 at 09:11
  • Ok, but [expr.const#5.8](https://eel.is/c++draft/expr.const#5.8) says that *if an operation that would have undefined behavior as specified in [intro] through [cpp] then expression `E` is not a core constant expression*. I think(IMO) this is fairly convoluted wording in the standard. Is this why there is a defect report 1952 that you mentioned? I mean if it is UB and so `E` is not a core constant expression then `E` cannot be used in a constantly evaluated context. – Jason May 10 '22 at 09:16
  • Additionally, if it(`E`) is a core constant expression, then it means there is no UB because if there would have been UB then `E` would not be a core constant expression. And so in the case `E` is a core constant expression the program should compile fine assuming there is not other error. – Jason May 10 '22 at 09:24
  • @AnoopRana `I think(IMO) this is fairly convoluted wording in the standard. Is this why there is a defect report 1952 that you mentioned?` That convoluted wording was added as a resolution to 1952. Before that wording, it seemed as if all UB would apply which was not the intention. – eerorika May 10 '22 at 09:34
  • Oh, i see. That makes sense. So that's why i was getting confused. In particular, i thought that they were referring to all UB while they were talking about only UB due to/as specified in [library] through [thread], or an invocation of the va_­start macro . Thanks for the clarification. – Jason May 10 '22 at 09:36