Why does a consteval function allow undefined behavior?
Asked Answered
T

1

19

There is a very neat property of constant expressions in C++: their evaluation cannot have undefined behavior (7.7.4.7):

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] of this document [ Note: including, for example, signed integer overflow ([expr.prop]), certain pointer arithmetic ([expr.add]), division by zero, or certain shift operations — end note ] ;

Trying to store the value of 13! in a constexpr int indeed yields a nice compile error:

constexpr int f(int n) 
{
    int r = n--;
    for (; n > 1; --n) r *= n;
    return r;
}

int main() 
{
    constexpr int x = f(13);
    return x;
}

Output:

9:19: error: constexpr variable 'x' must be initialized by a constant expression
    constexpr int x = f(13);
                  ^   ~~~~~
4:26: note: value 3113510400 is outside the range of representable values of type 'int'
    for (; n > 1; --n) r *= n;
                         ^
9:23: note: in call to 'f(3)'
    constexpr int x = f(13);
                      ^
1 error generated.

(BTW why does the error say "call to 'f(3)'", while it is a call to f(13)?..)

Then, I remove constexpr from x, but make f a consteval. According to the docs:

consteval - specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant

I do expect that such a program would again cause a compile error. But instead, the program compiles and runs with UB.

Why is that?

UPD: Commenters suggested that this is a compiler bug. I reported it: https://bugs.llvm.org/show_bug.cgi?id=43714

Tula answered 18/10, 2019 at 16:14 Comment(16)
in call to 'f(3)' - this is strange! Ex. If you put f(123) clang warns about in call to 'f(119)'.Sard
I think this is just a bug. The standard is clear that "an immediate invocation shall be a constant expression". However, it's also possible that there is something more complicated going on (i.e., maybe that requirement is going to be removed and Clang is implementing the new behaviour).Teague
!13 gets me a constant overflow warning, not error. VS2019++17. The wandbox link doesn't work for me so I don't know what compiler you are using.Impeachment
Compiler bug. Nothing to see here, move along.Autobiography
@Autobiography Maybe report the bug before moving along 😉Gutsy
@Impeachment Strange, the link works for me in multiple browsers... anyway, wandbox claims it uses clang from master, three days ago.Tula
@JesperJuhl Done.Tula
Doesn't C++20 change integers to assume 2's complement? Is the overflow still undefined?Christianna
@StoryTeller I don't think that paper made it in. Did it? If it did that would be good. Got a reference?Gutsy
@Jesper - Last I looked at the public mailing I thought I saw it as accepted. But I'm on mobile now, and checking it is incredibly hard. I will later if no one else does.Christianna
Ok, I tried it in Firefox, works. I use Seamonkey because of the mail. I guess it is just getting old.Impeachment
@StoryTeller Integers are two's complement, but overflow is still undefined.Minaret
@KamilCuk: It’s showing the value of the parameter at the moment of the overflow. (No, that’s not reasonable.)Mays
@DavisHerring Yeah, I'd filed 43715 for this too. That's a fun bug :-)Minaret
Those updates look more like answers than part of the question. Why were they edited into the question instead of posted as an answer?Forkey
@Forkey Ok, makes senseTula
T
3

This is a compiler bug. Or, to be more precise, this is an "underimplemented" feature (see the comment in bugzilla):

Yup - seems consteval isn't implemented yet, according to: https://clang.llvm.org/cxx_status.html

(the keyword's probably been added but not the actual implementation support)

Tula answered 19/10, 2019 at 10:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.