At least since Clang 5.0 (couldn't easily check back further) and still with recent Clang trunk: $ cat test.cc > #include <type_traits> > constexpr void f() {} > constexpr std::enable_if<true, void> g() {} $ clang++ -fsyntax-only -std=c++17 test.cc > test.cc:3:38: error: no return statement in constexpr function > constexpr std::enable_if<true, void> g() {} > ^ > 1 error generated. That is, it doesn't fail for f but only for g, and I don't find anything in C++17 [dcl.constexpr] that requires a return statement, so I assume the error is bogus? (I ran into this in some scenario with GCC libstdc++ after <https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=a858e2a4ea80631a88a5a1e351389236e3f35bc1> "Define std::__invoke_r for INVOKE<R>", which added a similar __can_invoke_as_void<...> __invoke_r(...) to libstdc++-v3/include/bits/inovke.h.)
The requirement for a return statement comes out of paragraph 5: "if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression [...] the program is ill-formed". A function with a non-void return type that doesn't return anything fails that rule. Of course, that should only apply to functions that have a non-void return type.
Oh, right... "std::enable_if<true, void>" isn't void, it's a class type. So clang is doing the right thing for your testcase: g() can't be called in a constexpr context. That said, there's still a related bug; the following should be accepted, I think: template<typename T> constexpr void g() {}
Err, sorry, wrong testcase; I meant to write: template<typename T> constexpr int g() {}
(In reply to Eli Friedman from comment #2) > Oh, right... "std::enable_if<true, void>" isn't void, it's a class type. So > clang is doing the right thing for your testcase: g() can't be called in a > constexpr context. Oops, right, I had started from a wrongly reduced test case (I should have used std::enable_if_t instead of std::enable_if) and then drew wrong conclusions. (See <https://gcc.gnu.org/ml/libstdc++/2019-05/msg00151.html> "Re: [PATCH] Define std::__invoke_r for INVOKE<R>" for details.)
(In reply to Eli Friedman from comment #2) > That said, there's still a related bug; the following should be accepted, I > think: > > template<typename T> constexpr void g() {} Nope (only for C++14 and later). (In reply to Eli Friedman from comment #3) > Err, sorry, wrong testcase; I meant to write: > > template<typename T> constexpr int g() {} Also nope :-) I'll fix the libstdc++ bug, but there's nothing wrong with clang.
Paragraph 5 only applies to a "constexpr function or constexpr constructor that is neither defaulted nor a template". I don't think clang is applying that; it's currently checking whether the return type is dependent. Not that it's likely to matter in practice, of course.
For templates the relevant rule is paragraph 6: "If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required." If the return type is non-dependent and not void and the function contains no return statement, then no call to the function could ever be part of a constant expression or constant initializer, so the definition is ill-formed, no diagnostic required. (Clang bothers to diagnose this because a diagnostic is required in C++11, and is correct in later language modes.)