https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93152
Bug ID: 93152 Summary: Order of template args in decl makes std::invocable fail Product: gcc Version: 10.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: db0451 at gmail dot com Target Milestone: --- This is as far as I've yet reduced this from my own project, compiling with `g++-10 (Debian 10-20191217-1) 10.0.0 20191217 (experimental) [trunk revision 279456]` From the comments below: // Putting Result as 1st template argument makes derived_from<WeirdBase> fail, // but putting derived_from<WeirdBase> 1st lets it compile without problem! // If you [constrain via static_assert] instead, both template arg orders work!! The failure occurs like so; it seems that somehow g++ concludes the wrong base for derived_from, and as mentioned that seems to be caused by template arg order >>> test3.cpp: In function ‘int main()’: test3.cpp:48:33: error: no match for call to ‘(const make_lambda(MemFun<WeirdSub1, Result, Args ...>) [with Result = void; WeirdSub1 = WeirdSub; Args = {}; MemFun<WeirdSub1, Result, Args ...> = void (WeirdSub::*)()]::<lambda(auto:1&&, auto:2&&)>) (WeirdSub, OtherSub)’ 48 | lambda( WeirdSub{}, OtherSub{} ); | ^ test3.cpp:28:9: note: candidate: ‘make_lambda(MemFun<WeirdSub1, Result, Args ...>) [with Result = void; WeirdSub1 = WeirdSub; Args = {}; MemFun<WeirdSub1, Result, Args ...> = void (WeirdSub::*)()]::<lambda(auto:1&&, auto:2&&)> [with auto:1 = WeirdSub; auto:2 = OtherSub]’ 28 | return [] | ^ test3.cpp:28:9: note: constraints not satisfied In file included from test3.cpp:1: /usr/include/c++/10/concepts: In instantiation of ‘make_lambda(MemFun<WeirdSub1, Result, Args ...>) [with Result = void; WeirdSub1 = WeirdSub; Args = {}; MemFun<WeirdSub1, Result, Args ...> = void (WeirdSub::*)()]::<lambda(auto:1&&, auto:2&&)> [with auto:1 = WeirdSub; auto:2 = OtherSub]’: test3.cpp:48:33: required from here /usr/include/c++/10/concepts:67:13: required for the satisfaction of ‘derived_from<auto:1, OtherSub>’ /usr/include/c++/10/concepts:67:28: note: ‘OtherSub’ is not a base of ‘WeirdSub’ 67 | concept derived_from = __is_base_of(_Base, _Derived) >>> Additionally, if you make the lambda instead take only derived_from<WeirdSub1>, and dutifully pass only that... then an ICE occurs instead. I'll probably file a 2nd, simpler MCVE for that - unless it turns out to have the same root cause. >>> test3.cpp: In function ‘int main()’: test3.cpp:48:21: internal compiler error: Segmentation fault 48 | lambda( WeirdSub{} );//, OtherSub{} ); | ^ 0xbecf3f crash_signal ../../src/gcc/toplev.c:328 0x7fb9a36f30ff ??? /build/glibc-dEjGnz/glibc-2.29/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 0x703e96 tsubst(tree_node*, tree_node*, int, tree_node*) ../../src/gcc/cp/pt.c:15083 0x707ac6 tsubst_template_args(tree_node*, tree_node*, int, tree_node*) ../../src/gcc/cp/pt.c:13099 0x625fa5 normalize_concept_check ../../src/gcc/cp/constraint.cc:685 0x625fa5 normalize_atom ../../src/gcc/cp/constraint.cc:719 0x625fa5 normalize_expression ../../src/gcc/cp/constraint.cc:748 0x6263da get_normalized_constraints ../../src/gcc/cp/constraint.cc:760 0x6263da get_normalized_constraints_from_info ../../src/gcc/cp/constraint.cc:778 0x629dfd get_normalized_constraints_from_decl ../../src/gcc/cp/constraint.cc:841 0x62a23a satisfy_declaration_constraints ../../src/gcc/cp/constraint.cc:880 0x62a578 constraint_satisfaction_value ../../src/gcc/cp/constraint.cc:2709 0x62a578 constraints_satisfied_p(tree_node*) ../../src/gcc/cp/constraint.cc:2730 0x5f7f11 add_function_candidate ../../src/gcc/cp/call.c:2280 0x5fa28d add_template_candidate_real ../../src/gcc/cp/call.c:3438 0x5fa7c4 add_template_candidate ../../src/gcc/cp/call.c:3479 0x5fa7c4 add_candidates ../../src/gcc/cp/call.c:5805 0x5ff654 add_candidates ../../src/gcc/cp/call.c:5720 0x5ff654 build_op_call_1 ../../src/gcc/cp/call.c:4768 0x5ff654 build_op_call(tree_node*, vec<tree_node*, va_gc, vl_embed>**, int) ../../src/gcc/cp/call.c:4864 >>> *** ``` #include <concepts> #include <type_traits> struct WeirdBase {}; struct WeirdSub: WeirdBase { void mem_fun() {} }; struct OtherBase {}; struct OtherSub: OtherBase {}; template <typename Object, typename Result, typename... Args> using MemFun = Result (Object::*)(Args...); // Putting Result as 1st template argument makes derived_from<WeirdBase> fail, #if 1 template <typename Result, std::derived_from<WeirdBase> WeirdSub1, typename... Args> // but putting derived_from<WeirdBase> 1st lets it compile without problem! #else template <std::derived_from<WeirdBase> WeirdSub1, typename Result, typename... Args> #endif auto make_lambda( MemFun<WeirdSub1, Result, Args...> ) { return [] #if 1 (std::derived_from<WeirdSub1> auto&&, std::derived_from<OtherBase> auto&&) { // If you do the following instead, both template arg orders work!! #else (std::derived_from<WeirdBase> auto&& weird, std::derived_from<OtherBase> auto&&) { using WeirdSub2 = std::remove_reference_t< decltype(weird) >; static_assert( std::derived_from<WeirdSub2, WeirdSub1> ); #endif }; } auto main() -> int { auto const lambda = make_lambda(&WeirdSub::mem_fun); lambda( WeirdSub{}, OtherSub{} ); return 0; } ```