1

I've been experimenting with different forms of UB in order to better understand the current compiler inadequacies using constexpr evaluations to detect UB.

Note: There is normally no compiler requirement to emit diagnostics when UB occurs. However, this is not the case when executing compile time code such as consteval functions as shown here. Even there, there is an exception for library code where compilers are not required to emit diagnostics for UB. The question here does not use library code and compilers should emit diagnostics when encountering UB executing code at compile time.

I ran across an odd situation where modifying the underlying memory of an const int in automatic storage (aka stack) results in no warnings with CLANG, GCC, and MSVC but when evaluated in a constexpr function CLANG and GCC elicit the expected error while MSVC gives no error.

The function being evaluated casts a const int to a non const through it's address to attempt to change it's value from 1 to 2. Afterwards, the const value is added to that returned from a function called with a const int& that simply returns the argument value.

This is all UB, of course. However, all three compilers happily compile and run when not in a constexpr function. While UB, they are not required to emit diagnostics. And they interpret the result as the original const value when the variable is used directly but as the modified value when accessed through a reference.

But when running in compile time code, GCC and CLANG identify the UB as, for example:

modification of object of const-qualified type 'const int' is not allowed in a constant expression

MSVC produces no error or warning.

Since this uses no library code in the constexpr evaluation, I believe MSVC is in error not reporting the UB. Is this correct?

Compiler Explorer

#include <iostream>
// un-comment out the next line to evaluate at runtime
// #define consteval

consteval int ret_arg(const int& v) { return v; }

consteval int f()
{
    const int i{ 1 };
    *const_cast<int*>(&i) = 2;
    return i + ret_arg(i);
}

int main()
{
    std::cout << f() << "\n";
}

Run time UB w/o compiler errors for all compilers can be seen by commenting the "#define consteval" line at the top.

doug
  • 3,840
  • 1
  • 14
  • 18
  • 2
    UB is UB. Not sure this is a useful pursuit – Taekahn Sep 25 '22 at 22:52
  • Yes, this is a MSVC bug. – Drew Dormann Sep 25 '22 at 22:55
  • "E3133:call to consteval function "f" did not produce a valid constant expression". Since IntelliSense parser errors should match compile error message you can use Help > Send Feedback to report the problem. – Hans Passant Sep 25 '22 at 23:43
  • @Taekahn I've come to use constexpr eval to check for UB as it's pretty good, generally at locating bits of UB I'd missed. That said, I've seen a lot of missing holes in the different compilers. My intent is to determine areas I can focus on to prevent UB where the compilers are least effective at flagging it. What's more problematic are false UB positives. They take more time to track. – doug Sep 26 '22 at 00:27
  • @DrewDormann who said that? [The C++ doesn't require any diagnostic message for UB](https://stackoverflow.com/q/24881706/995714). Do you see a warning for an overflow at runtime? It's UB and no compiler will complain about that unless you're using Ubsan – phuclv Sep 26 '22 at 14:20
  • Does this answer your question? [Why doesn't the compiler warn you if there is possible Undefined Behaviour?](https://stackoverflow.com/questions/37942101/why-doesnt-the-compiler-warn-you-if-there-is-possible-undefined-behaviour) – phuclv Sep 26 '22 at 14:21
  • 1
    @phuclv there are more stringent rules when an expression is constant-evaluated. The code is then ill-formed, and that requires a diagnostic message. This question is about the use of that rule. This is not a question about runtime behavior. – Drew Dormann Sep 26 '22 at 14:37

0 Answers0