8

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.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • Does it work if f is constexpr? (when forcing constexpr, like `constexpr int x = (f(), 5);`) – Daniel Aug 11 '20 at 19:22
  • 3
    clang does [not yet claim to implement `consteval`](https://clang.llvm.org/cxx_status.html#cxx20). – Barry Aug 11 '20 at 19:22
  • @Dani No, it gives the same [error](https://godbolt.org/z/Tv5Y5f). – cigien Aug 11 '20 at 19:24
  • @Barry True, but I was under the impression that it's been implemented in trunk for a while. So it's just an incomplete feature then? Still an odd error though. – cigien Aug 11 '20 at 19:26
  • Then I would say it’s a compiler bug. The rationale is `std::sort` for instance. – Daniel Aug 11 '20 at 19:27
  • Does this mean clang doesn’t support constexpr sort right now? It’s a c++20 feature, so they don’t have to yet. – Daniel Aug 11 '20 at 19:30
  • @Dani Yes, I don't think `constexpr` `sort` is supported yet. But how is that relevant here? – cigien Aug 11 '20 at 19:35
  • @Dani What does `sort` have to do with `consteval`? It's not a `consteval` function. – Barry Aug 11 '20 at 19:35
  • BTW: Whats is the sense of consteval with a void function? – Klaus Aug 11 '20 at 19:58
  • @Klaus Ha, good question, didn't think of that :p I guess it could be useful to have a named function that does a bunch of `static_assert`s. – cigien Aug 11 '20 at 20:03
  • @Klaus: The function can still modify its arguments if they are declared in a constexpr/eval function. For example, std::sort. – Daniel Aug 11 '20 at 20:40
  • @Barry: it’s about the situation in the comments, where f is constexpr – Daniel Aug 11 '20 at 20:42
  • @Dani: Perfect! Thats it! Thanks! – Klaus Aug 11 '20 at 20:42
  • @Klaus Yes, arguments can be modified. I was thinking of a void function with no arguments, as in the question. Use cases for that are less obvious. – cigien Aug 11 '20 at 20:43

2 Answers2

8

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.

Richard Smith
  • 13,696
  • 56
  • 78
  • Indeed, it now compiles on [trunk](https://godbolt.org/z/TEq3fx). Thanks :) – cigien Sep 21 '20 at 01:14
  • Yes, it compiles now, but a linker error occurs: https://stackoverflow.com/questions/69513993/immediate-functions-returning-void-in-clang – Fedor Oct 10 '21 at 10:15
  • It's got nothing to do with void. It's the context of the call. I get an error with unsigned long long. But only in a particular context, the same function works fine in other contexts. – Yttrill Dec 05 '22 at 03:49
  • ```static consteval Nat size() { return H::size() * Product::size(); } Product succ() const { Nat x = size(); return Product((rep + 1) % x); }ring.cxx:399:34: error: call to consteval function 'Product::size' is not a constant expression Product succ() const { Nat x = size(); return Product((rep + 1) % x); }``` – Yttrill Dec 05 '22 at 03:54
0

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.

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • 1
    _The "if any" seems to make void valid here._ "if any" covers constructors' and destructors' absence of return types. `void f();` **does have** a return type. «_cv_ `void`» is a [literal type](https://timsong-cpp.github.io/cppwp/n4861/basic.types#def:literal_type), "if any" is not for it. – Language Lawyer Aug 11 '20 at 20:22
  • @LanguageLawyer: Thanks for the explanation. That makes void a valid return type for a conexpr/consteval function. – Klaus Aug 11 '20 at 20:32
  • @LanguageLawyer how do you construct an object of `void`? It literally received that name from K&R for being nothing. I guess that's wher clang's architecture fails – Swift - Friday Pie Aug 11 '20 at 20:33
  • Actually, I don't doubt that the definition of `f` is fine. The question is about the call `f()` which is where the error comes from. Note that clang allows calling `f` from other `consteval` functions. [demo](https://godbolt.org/z/4P1TTb). – cigien Aug 11 '20 at 20:37
  • _"if any" covers constructors' and destructors' absence of return types_ Actually, only constructors' absence, destructors are not allowed to be `consteval`. – Language Lawyer Aug 11 '20 at 20:42
  • I edited the question a bit with possibly relevant information, if you could take a look. – cigien Aug 11 '20 at 20:50
  • @cigien: I can call consteval or constexpr funcs from non consteval/constexpr functions always.I did not read about any restriction in that direction. Typically a compiler optimizes that call out. – Klaus Aug 11 '20 at 20:54
  • No, there are language restrictions on calling functions from certain contexts that have nothing to do with compiler optimizations. – cigien Aug 11 '20 at 21:14