While experimenting on this, I've hit what seems to be a bug in GCC.
Here's the code triggering it
#include <type_traits>
constexpr auto f = [](int){};
template<typename T>
auto canCallFOn(T) -> decltype([]{ f(T{}); }(), std::true_type{});
constexpr std::false_type canCallFOn(...) {
return {};
}
int main() {
static_assert(!canCallFOn(1));
static_assert(!canCallFOn(""));
}
I would have thought that the non-template overload would be called when the templated one can't be (captain obvious), but that the first could be called whenever []{ f(T{}); }() makes sense in the context of the instantiation, but given the errors below, I'm more inclined to thing it's invalid code.
Is it indeed invalid code? If so, why? And what's so special about it that it causes an internal compiler error?
Compiling it with g++ -std=c++20 source.cpp fails like this:
source.cpp:13:19: error: non-constant condition for static assertion
13 | static_assert(!canCallFOn(1));
| ^~~~~~~~~~~~~~
source.cpp:13:30: error: call to non-‘constexpr’ function ‘decltype ((<lambda>(), std::true_type{})) canCallFOn(T) [with T = int; decltype ((<lambda>(), std::true_type{})) = std::integral_constant<bool, true>; std::true_type = std::integral_constant<bool, true>]’
13 | static_assert(!canCallFOn(1));
| ~~~~~~~~~~^~~
source.cpp:6:6: note: ‘decltype ((<lambda>(), std::true_type{})) canCallFOn(T) [with T = int; decltype ((<lambda>(), std::true_type{})) = std::integral_constant<bool, true>; std::true_type = std::integral_constant<bool, true>]’ declared here
6 | auto canCallFOn(T) -> decltype([]{ f(T{}); }(), std::true_type{});
| ^~~~~~~~~~
source.cpp: In substitution of ‘template<class T> decltype ((<lambda>(), std::true_type{})) canCallFOn(T) [with T = const char*]’:
source.cpp:14:30: required from here
source.cpp:6:37: error: no match for call to ‘(const<lambda(int)>) (const char*)’
6 | auto canCallFOn(T) -> decltype([]{ f(T{}); }(), std::true_type{});
| ~^~~~~
source.cpp:6:37: note: candidate: ‘void (*)(int)’ (conversion)
source.cpp:6:37: note: conversion of argument 2 would be ill-formed:
source.cpp:6:37: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
6 | auto canCallFOn(T) -> decltype([]{ f(T{}); }(), std::true_type{});
| ~^~~~~
| |
| const char*
source.cpp:3:20: note: candidate: ‘<lambda(int)>’ (near match)
3 | constexpr auto f = [](int){};
| ^
source.cpp:3:20: note: conversion of argument 1 would be ill-formed:
source.cpp:6:37: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
6 | auto canCallFOn(T) -> decltype([]{ f(T{}); }(), std::true_type{});
| ~^~~~~
| |
| const char*
source.cpp:14:19: error: non-constant condition for static assertion
14 | static_assert(!canCallFOn(""));
| ^~~~~~~~~~~~~~~
‘
internal compiler error: error reporting routines re-entered.
0x1a02e48 error(char const*, ...)
???:0
0x68b641 build_op_call(tree_node*, vec<tree_node*, va_gc, vl_embed>**, int)
???:0
0x810f2d finish_call_expr(tree_node*, vec<tree_node*, va_gc, vl_embed>**, bool, bool, int)
???:0
0x7e1f0c tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool)
???:0
0x7f805c tsubst_lambda_expr(tree_node*, tree_node*, int, tree_node*)
???:0
0x7e213f tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool)
???:0
0x7e06a7 tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool)
???:0
0x7e09af tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool)
???:0
0x7e3fd9 tsubst(tree_node*, tree_node*, int, tree_node*)
???:0
0x19fb329 pp_format(pretty_printer*, text_info*)
???:0
0x1a00e88 diagnostic_report_diagnostic(diagnostic_context*, diagnostic_info*)
???:0
0x1a032fc error_at(unsigned int, char const*, ...)
???:0
0x6a5f1c require_rvalue_constant_expression(tree_node*)
???:0
0x7c7a9c c_parse_file()
???:0
0x8dcedd c_common_parse_file()
???:0
Please submit a full bug report, with preprocessed source (by using -freport-bug).
Please include the complete backtrace with any bug report.
See <https://bugs.archlinux.org/> for instructions.