24

Clang accepts the following code, but gcc rejects it.

void h() { }

constexpr int f() {
    return 1;
    h();
}

int main() {
    constexpr int i = f();
}

Here is the error message:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'constexpr int f()':
main.cpp:5:6: error: call to non-'constexpr' function 'void h()'
     h();
     ~^~
main.cpp: In function 'int main()':
main.cpp:9:24: error: 'constexpr int f()' called in a constant expression
     constexpr int i = f();
                       ~^~
main.cpp:9:19: warning: unused variable 'i' [-Wunused-variable]
     constexpr int i = f();

This could well be the case where both compilers are correct, once we consider [dcl.constexpr]/5, given that f() is not a constant expression, as it doesn't satisfy [expr.const]/(4.2), as it calls a non-constexpr function h. That is, the code is ill-formed, but no diagnostic is required.

One other possibility is that the code is well formed, as [expr.const]/(4.2) doesn't apply in this case because the call to h in f is not evaluated. If this is the case, gcc is wrong and clang is correct.

Barry
  • 286,269
  • 29
  • 621
  • 977
Alexander
  • 2,581
  • 11
  • 17
  • 6
    Clang does not allow calling `h()` before returning, so the real question here is: *Is a compiler allowed to ignore dead ill-formed code?* – idmean Apr 18 '19 at 19:12
  • "as it calls a non-constexpr function `h`". But it doesn't actually call `h`. I'd say that gcc is wrong here. – geza Apr 18 '19 at 19:15
  • I'm adding the C++14 tag since on C++11 there's no question that it's ill-formed. – Barry Apr 18 '19 at 19:29
  • 1
    This code works in GCC's trunk.. https://godbolt.org/z/f04MCq and https://godbolt.org/z/bAyE8a .. So it's already fixed.. Free upvotes. If compiled with GCC 8.3 it will fail but compile with Trunk and it works fine. – Brandon Apr 18 '19 at 19:31

1 Answers1

24

Clang is correct. A call to f() is a constant expression since the call to h() is never evaluated, so [dcl.constexpr]/5 doesn't apply. The call to h() in the body of f() is not ill-formed because the constraints on constexpr functions don't say anything about not being allowed to call non-constexpr functions. Indeed, a function like the following is well-formed because a call to it can be a constant expression when x is odd:

constexpr int g(int x) {
    if (x%2 == 0) h();
    return 0;
}
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • You answer seems to be correct, but I don't agree with your reasoning. Consider this: `constexpr int f() { h(); return 1; }`. In this case the code is ill-formed NDR according to [\[dcl.constexpr\]/5](http://eel.is/c++draft/dcl.constexpr#5) because the function `f` invokes a non-constexpr function `h`, and this is not allowed according to [\[expr.const\]/(4.2)](http://eel.is/c++draft/expr.const#4.2). See also [this question](https://stackoverflow.com/q/34272899/2337207) in SO. – Alexander Apr 18 '19 at 20:34
  • 1
    @Alexander In the case where the call to `h()` is before the return statement, every call to `f` will fail to be a constant expression, and [dcl.constexpr]/5 applies. In the case where the call to `h()` is after the return statement, every call to `f` will be a constant expression, so [dcl.constexpr]/5 does not apply. – Brian Bi Apr 18 '19 at 20:36
  • Ok, but why did you say "The call to h() in the body of f() is not ill-formed because the constraints on constexpr functions don't say anything about not being allowed to call non-constexpr functions."? This is what is confusing in your answer. – Alexander Apr 18 '19 at 20:42
  • 5
    @Alexander I'm not sure what part of that was unclear. I linked a section of the standard that lists the constructs that are forbidden from appearing in a `constexpr` function's body. A call to a non-`constexpr` function is not one of the forbidden constructs. However, if the call to the non-`constexpr` function becomes inevitable (i.e., occurs along all paths) then [dcl.constexpr]/5 becomes violated. – Brian Bi Apr 18 '19 at 20:47
  • Well, I suppose you could resume your answer to just this: "Clang is correct. A call to `f()` is a constant expression since the call to `h()` is never evaluated, so [dcl.constexpr]/5 doesn't apply." But I'm still missing a quote from the standard confirming that the call to `h()` is never evaluated. For example, [dcl.constexpr]/5 doesn't say anything about this. – Alexander Apr 19 '19 at 15:13
  • 1
    @Alexander According to the rules of the language, the `return` statement returns control to the caller, skipping all remaining statements in the function. What about this is unclear? – Brian Bi Apr 25 '19 at 07:51