clang says call to void consteval function is not a constant expression

Viewed 1864

clang(trunk) gives an error for the following code:

consteval void f() {}

int main() 
{ 
    f();  // error: call to consteval function 'f' is not a constant expression
          // note: subobject of type 'void' is not initialized
}

while gcc(trunk) compiles this without error.

I feel this is probably a clang bug, since both gcc and clang accept this code:

consteval int g() { return 42; }

int main() 
{ 
    g();  // ok
}

Here's the code to play with.

So is this a clang bug, or is the code ill-formed, or have ub, or something else?


Edit: I feel it might be relevant to point out that clang allows calling f from other functions if they are also consteval. It only gives the error when f is called from non-consteval functions:

consteval int h() 
{ 
    f();       // ok
    return 42; 
}

demo.

2 Answers

This was a Clang bug that was both introduced between version 10 and version 11, and fixed last month. The implementation of consteval in Clang is mostly, but not entirely, complete, and this bug arose after one of the patches adding more complete consteval support.

Details: the top-level entry point into Clang's constant evaluator checks whether the result is a permitted result of a constant expression -- it checks that the result doesn't contain pointers to automatic storage duration or a temporary or similar. But this check was never updated to accommodate for void being a literal type, and would reject values of type void for being "uninitialized". This was never noticed before the addition of consteval support, because all top-level constant evaluations were of non-void types.

What I found c++20 final draft is:

9.2.5 The constexpr and constevals pecifiers[dcl.constexpr] (2) A constexpr or consteval specifier used in the declaration of a function declares that function to be a constexpr function. A function or constructor declared with the consteval specifier is called animmediate function. A destructor, an allocation function, or a deallocation function shall not be declared with the consteval specifier.

(3) The definition of a constexpr function shall satisfy the following requirements:

3.1 its return type (if any) shall be a literal type;

and

6.8 Types [basic.types]

(10) A type is a literal type if it is :

(10.1) cv void

...

As this void is a valid return type for a consteval function.

Related