From 7915434e98d7378223db344d59802a77c3733f8a Mon Sep 17 00:00:00 2001 From: Christof Meerwald Date: Tue, 20 Oct 2026 16:00:50 +0000 Subject: [PATCH] CTAD through a template template parameter of an alias template [GH #173] --- src/Changes | 22 + src/il.c | 22 +- src/il.h | 6 +- src/il_def.h | 8 +- src/overload.c | 16 +- src/templates.c | 153 +++- .../tests/changes/.gh-173.rto/default.1.1.txt | 1 + tests/tests/changes/gh-173.sft.cpp | 8 + .../regressions/.gh-173.rto/default.1.1.txt | 1 + .../regressions/.gh-173.rto/default.2.1.txt | 1 + .../regressions/.gh-173.rto/default.3.1.txt | 1 + .../regressions/.gh-173.rto/default.4.1.txt | 1 + tests/tests/regressions/gh-173.sft.cpp | 686 ++++++++++++++++++ 13 files changed, 881 insertions(+), 45 deletions(-) create mode 100644 tests/tests/changes/.gh-173.rto/default.1.1.txt create mode 100644 tests/tests/changes/gh-173.sft.cpp create mode 100644 tests/tests/regressions/.gh-173.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-173.rto/default.2.1.txt create mode 100644 tests/tests/regressions/.gh-173.rto/default.3.1.txt create mode 100644 tests/tests/regressions/.gh-173.rto/default.4.1.txt create mode 100644 tests/tests/regressions/gh-173.sft.cpp diff --git a/src/Changes b/src/Changes index 559372f7b0..db5c28254b 100644 --- a/src/Changes +++ b/src/Changes @@ -12,6 +12,28 @@ ref-qualifiers. They now leave such types unchanged. For example, with using F = __add_pointer(F); // Previously a spurious error, now okay. +10/20/26 [GH #173] +CTAD through a template template parameter of an alias template + +With the changes for EDGcpfe/28857 (in version 6.9), a decltype in an alias +template that deduces class template arguments through one of the alias +template's template template parameters could trigger an internal error due to +a failed assertion in generic_cast_operand, when the alias was used with +dependent arguments, for example in a requires expression. For example, with +--c++20: + + template struct S { S(T); }; + template class TT> using A = decltype(TT(0)); + template class TT> + constexpr bool b = requires { typename A; }; + static_assert(b); // Previously an internal error, now okay. + +In addition, deduction now succeeds when the template bound to the template +template parameter has more parameters (with default arguments or packs) than +the parameter itself, and a mismatch is now treated as a deduction failure +rather than an error in contexts where errors are not issued. + + 10/19/26 [GH #218] Reachability after a temporary with a "noreturn" constructor or destructor diff --git a/src/il.c b/src/il.c index 662be45c4e..0888ffd46d 100644 --- a/src/il.c +++ b/src/il.c @@ -11172,11 +11172,15 @@ vk_ext, ...) and, for vk_ext, its size_constant. #endif /* GNU_VECTOR_TYPES_ALLOWED */ a_type_ptr make_class_template_placeholder(a_symbol_ptr class_template, - a_source_position *pos) + a_source_position *pos, + /* Defaulted: */ a_symbol_ptr bound_template) /* Create a type entry representing a class template used for C++ 17 class template argument deduction. The result is a special kind of tk_template_param -entry. pos is used to establish the type entry's position information. +entry. pos is used to establish the type entry's position information. When +class_template is a type template template parameter, bound_template (or the +current argument bound to class_template if bound_template is NULL) is recorded +in the supplement's template_symbol field. */ { a_symbol_ptr sym; @@ -11188,6 +11192,16 @@ entry. pos is used to establish the type entry's position information. a_template_symbol_supplement_ptr tssp = template_supplement_for_symbol(class_template); + if (bound_template == NULL) { + /* When not created by placeholder substitution, check whether an enclosing + instantiation has bound an argument to a template template parameter + (when unbound, argument_template points to class_template itself). */ + a_symbol_ptr arg_sym = template_argument_if_template_template_param( + class_template); + if (arg_sym != class_template) { + bound_template = arg_sym; + } /* if */ + } /* if */ if (!alias_ctad_enabled && alias_ctad_allowed && is_alias_template_symbol(class_template)) { if (clang_mode || @@ -11210,7 +11224,11 @@ entry. pos is used to establish the type entry's position information. sym->decl_scope = class_template->decl_scope; sym->variant.type.ptr = type; tpcp->depth = CLASS_TEMPLATE_PLACEHOLDER_NESTING_DEPTH; + /* For a template template parameter, CTAD keeps using the parameter's own + parameter list and defaults, while substitution goes through the template + bound to it. */ tptsp->constraint.class_template_symbol = class_template; + tptsp->template_symbol = bound_template; set_type_size(type); set_source_corresp(&type->source_corresp, sym); if (!class_template->is_error && diff --git a/src/il.h b/src/il.h index 80f99a206f..913487a57e 100644 --- a/src/il.h +++ b/src/il.h @@ -3899,8 +3899,10 @@ a routine. (con)->variant.address.offset == 0 && !(con)->implicit_cast) extern -a_type_ptr make_class_template_placeholder(struct a_symbol *class_template, - a_source_position *pos); +a_type_ptr make_class_template_placeholder( + struct a_symbol *class_template, + a_source_position *pos, + struct a_symbol *bound_template = NULL); extern a_type_ptr make_auto_type(a_source_position *pos, a_boolean is_decltype_auto); diff --git a/src/il_def.h b/src/il_def.h index b063cb1ec2..012805beb7 100644 --- a/src/il_def.h +++ b/src/il_def.h @@ -9358,9 +9358,11 @@ typedef struct a_template_param_type_supplement { *template_symbol; /* For templates (including C++/CLI generics) points to the sk_class_template or sk_function_template - symbol of which this is a type parameter. NULL - otherwise. Used in the front end only; cannot - be used in back ends. */ + symbol of which this is a type parameter. For a + placeholder type naming a type template template + parameter, points to the bound template argument, + if any. NULL otherwise. Used in the front end + only; cannot be used in back ends. */ #if MICROSOFT_EXTENSIONS_ALLOWED a_generic_constraint_ptr generic_constraints; diff --git a/src/overload.c b/src/overload.c index 57d3973e66..866e72bb56 100644 --- a/src/overload.c +++ b/src/overload.c @@ -30972,11 +30972,14 @@ set to TRUE and FALSE is returned. if (is_template_template_param_symbol(ct_sym)) { /* Class template argument deduction for a template template parameter: use guides synthesized from the class or alias template argument. */ - a_symbol_ptr bound_tttp_arg_sym; + a_symbol_ptr bound_tttp_arg_sym, target_sym; a_boolean is_deducible_template; + target_sym = placeholder_type->variant.template_param.extra_info + ->template_symbol; bound_tttp_arg_sym = bound_template_template_argument( - ct_sym, &is_deducible_template); + target_sym != NULL ? target_sym : ct_sym, + &is_deducible_template); if (bound_tttp_arg_sym == NULL) { /* Still dependent. */ *still_dependent = TRUE; @@ -30984,7 +30987,10 @@ set to TRUE and FALSE is returned. goto done; } else if (!is_deducible_template || bound_tttp_arg_sym->is_error) { /* Template argument does not name a deducible template. */ - pos_sy_error(ec_no_class_template_guide, source_pos, bound_tttp_arg_sym); + if (expr_error_should_be_issued()) { + pos_sy_error(ec_no_class_template_guide, source_pos, + bound_tttp_arg_sym); + } /* if */ *still_dependent = FALSE; result = FALSE; goto done; @@ -31046,7 +31052,9 @@ set to TRUE and FALSE is returned. guide_set = ct_tssp->variant.class_template.deduction_guides; } /* if */ if (guide_set == NULL) { - pos_sy_error(ec_no_class_template_guide, source_pos, ct_sym); + if (expr_error_should_be_issued()) { + pos_sy_error(ec_no_class_template_guide, source_pos, ct_sym); + } /* if */ } else { selected_sym = select_overloaded_function( guide_set, diff --git a/src/templates.c b/src/templates.c index 41a501ed64..a3a732d79d 100644 --- a/src/templates.c +++ b/src/templates.c @@ -2381,6 +2381,10 @@ template parameters. init_decl_parse_state(&dps); init_templ_decl_state(&decl_state, &dps); decl_state.il_template_entry = alloc_template(); + /* The IL template entry and its source correspondence are recorded in the + symbol so that the CTAD deducibility checks can access them. */ + tssp->il_template_entry = decl_state.il_template_entry; + set_source_corresp(&decl_state.il_template_entry->source_corresp, sym); decl_state.decl_info = tdip; create_prototype_type(&decl_state, sym, tssp, (a_symbol_ptr)NULL, /*is_partial_specialization=*/FALSE); @@ -15381,7 +15385,8 @@ Otherwise, return the original template. coordinates = &templ->coordinates; tap = get_template_arg_for_coordinates(coordinates, options, &templ_arg_list, templ_param_list); - if (tap == NULL || tap->variant.templ.ptr == NULL) { + if (tap == NULL || !is_template_templ_arg(tap) || + tap->variant.templ.ptr == NULL) { /* No value has been provided for this template parameter yet. Don't do the substitution, but don't consider this to be a copy error either. */ @@ -17356,30 +17361,34 @@ static a_type_ptr copy_class_template_placeholder_with_substitution( /* The given type is a class template placeholder type. Perform substitution on the template associated with the placeholder type using the given -template argument list and return the updated placeholder type. See -copy_type_with_substitution for the meaning of the parameters. +template argument list and return the updated placeholder type, or type +itself if no change is needed. See copy_type_with_substitution for the +meaning of the parameters. */ { - a_symbol_ptr ct_sym = type->variant.template_param.extra_info - ->constraint.class_template_symbol; + a_template_param_type_supplement_ptr + tptsp = type->variant.template_param.extra_info; + a_symbol_ptr ct_sym = tptsp->constraint.class_template_symbol; + a_symbol_ptr target_sym = tptsp->template_symbol != NULL ? + tptsp->template_symbol : ct_sym; a_template_ptr orig_templ, templ; - a_type_ptr result = NULL; + a_type_ptr result = type; - check_assertion(is_class_template_symbol(ct_sym)); - orig_templ = ct_sym->variant.template_info->il_template_entry; + check_assertion(is_class_template_symbol(ct_sym) && + is_class_template_symbol(target_sym)); + orig_templ = target_sym->variant.template_info->il_template_entry; templ = copy_template_with_substitution(orig_templ, templ_arg_list, templ_param_list, source_pos, options, copy_error, ctws_state); if (!*copy_error && templ != orig_templ) { - a_symbol_ptr new_ct_sym = symbol_for(templ); + a_symbol_ptr new_target_sym = symbol_for(templ); - if (is_template_template_param_symbol(ct_sym) && - !is_template_template_param_symbol(new_ct_sym)) { - /* Keep the type template template parameter as the placeholder target; - CTAD still needs its parameter list and default arguments. */ - new_ct_sym = ct_sym; + if (is_template_template_param_symbol(ct_sym)) { + result = make_class_template_placeholder(ct_sym, source_pos, + new_target_sym); + } else { + result = make_class_template_placeholder(new_target_sym, source_pos); } /* if */ - result = make_class_template_placeholder(new_ct_sym, source_pos); } /* if */ return result; } /* copy_class_template_placeholder_with_substitution */ @@ -18033,6 +18042,11 @@ a pointer over a reference type or creating an array of references. } /* if */ } /* if */ break; + } else if (!tptk_is(type, tptk_param)) { + /* This is not an actual template parameter (e.g., a tptk_member or + tptk_unknown type); there is no argument to substitute. */ + new_type = type; + break; } /* if */ /* If this template parameter type entry corresponds to the nth parameter, the real type to substitute for it is given in the nth @@ -45349,6 +45363,74 @@ Return TRUE if params includes the template parameter whose symbol is sym. } /* template_param_list_contains_symbol */ +static a_boolean add_trailing_default_template_args( + a_symbol_ptr arg_sym, + a_template_param_ptr tttp_params, + a_template_arg_ptr *p_args, + a_source_position *source_pos) +/* +Append the default arguments of the trailing parameters of arg_sym to *p_args, +the prototype argument list of a type template template parameter. arg_sym is +the template bound to that parameter, and tttp_params is its parameter list. +Nothing is done if tttp_params contains a pack. Return TRUE if *p_args is a +valid argument list for arg_sym, and FALSE otherwise. source_pos is used for +substitution. +*/ +{ + a_template_param_ptr tpp, arg_params; + a_template_arg_ptr tap, last_tap = NULL; + a_boolean has_pack = FALSE, copy_error = FALSE; + + for (tpp = tttp_params; tpp != NULL; tpp = tpp->next) { + if (tpp->is_pack) has_pack = TRUE; + } /* for */ + arg_params = template_supplement_for_symbol(arg_sym) + ->cache->decl_info->parameters; + tpp = arg_params; + for (tap = *p_args; tap != NULL && tpp != NULL; tap = tap->next) { + last_tap = tap; + if (!tpp->is_pack) { + tpp = tpp->next; + } /* if */ + } /* for */ + if (!has_pack && tap != NULL) { + copy_error = TRUE; + } /* if */ + for (; !has_pack && !copy_error && tpp != NULL && tpp->has_default_arg && + !tpp->is_pack; + tpp = tpp->next) { + a_ctws_state ctws_state; + + tap = alloc_template_arg( + templ_arg_kind_for_symbol_kind(tpp->param_symbol->kind)); + get_template_arg_value_from_default(arg_sym, tap, tpp, arg_params); + if (tpp->def_arg_involves_template_param) { + /* The default can refer to earlier parameters of arg_sym. */ + init_ctws_state(&ctws_state); + substitute_template_argument(tap, tpp, *p_args, arg_params, + *p_args, arg_params, source_pos, + CTWS_MAY_BE_RESCANNED, + /*is_generic=*/FALSE, ©_error, + &ctws_state); + free_list_of_variadic_param_info(ctws_state.variadic_param_info); + } /* if */ + if (copy_error) { + free_template_arg_list(tap); + } else if (last_tap == NULL) { + *p_args = tap; + last_tap = tap; + } else { + last_tap->next = tap; + last_tap = tap; + } /* if */ + } /* for */ + if (!has_pack && !copy_error && tpp != NULL && tpp->is_pack) { + tpp = tpp->next; + } /* if */ + return has_pack || (!copy_error && tpp == NULL); +} /* add_trailing_default_template_args */ + + static a_type_ptr make_tttp_ctad_defining_type( a_symbol_ptr tttp_sym, a_template_param_ptr tttp_params, @@ -45362,13 +45444,18 @@ tttp_params. Return NULL if that type cannot be formed. */ { a_template_arg_ptr proto_args; - a_symbol_ptr inst_sym; + a_symbol_ptr inst_sym = NULL; a_type_ptr result = NULL; proto_args = create_prototype_arg_list(tttp_sym, tttp_params, /*add_pack_descr=*/TRUE); push_instantiation_scope_for_rescan(arg_sym); - inst_sym = find_template_class_simple(arg_sym, &proto_args); + /* Supply defaults for any extra parameters of the bound template, and treat + an invalid argument list as a deduction failure. */ + if (add_trailing_default_template_args(arg_sym, tttp_params, &proto_args, + &tttp_sym->decl_position)) { + inst_sym = find_template_class_simple(arg_sym, &proto_args); + } /* if */ pop_instantiation_scope_for_rescan(); if (inst_sym != NULL && is_type_symbol(inst_sym)) { result = type_symbol_type(inst_sym); @@ -45820,17 +45907,13 @@ On success, return the symbol for the new guide; otherwise return NULL. new_guide->decl_seq = guide_sym->decl_seq; new_tssp = new_guide->variant.template_info; new_tssp->cache->decl_info->parameters = new_template_params; - if (kind == actck_alias_template) { + if (kind == actck_alias_template || + kind == actck_template_template_param) { /* Create an expression to check whether the alias template parameters are deducible from the return type of the transformed guide. */ is_deducible_expr = make_builtin_edg_is_deducible_expr( alias_tssp->il_template_entry, ret_type); - } else if (kind == actck_template_template_param) { - a_template_ptr result_ct_templ = - template_supplement_for_symbol(result_ct_sym)->il_template_entry; - is_deducible_expr = make_builtin_edg_is_deducible_expr(result_ct_templ, - ret_type); } /* if */ guide_template_decl = guide_tssp->variant.function.decl_cache-> decl_info->template_decl; @@ -45904,17 +45987,12 @@ On success, return the symbol for the new guide; otherwise return NULL. } else { /* For the non-template case, perform the deducible-from check for alias templates and for type template template parameters. */ - if (kind == actck_alias_template && + if ((kind == actck_alias_template || + kind == actck_template_template_param) && !is_template_deducible_from(alias_tssp->il_template_entry, ret_type)) { goto done; } /* if */ - if (kind == actck_template_template_param && - !is_template_deducible_from( - template_supplement_for_symbol(result_ct_sym)->il_template_entry, - ret_type)) { - goto done; - } /* if */ new_guide = alloc_symbol(sk_routine, (kind == actck_template_template_param ? alias_like_template->header : @@ -46028,6 +46106,7 @@ guides of arg_sym. a_symbol_ptr guide_set, guide_sym, invented_alias_sym; a_template_param_ptr tttp_params; a_type_ptr defining_type; + a_template_ptr invented_alias_templ; a_boolean is_list = FALSE; if (result_guide_list == NULL) goto done; @@ -46043,6 +46122,11 @@ guides of arg_sym. invented_alias_sym = make_invented_class_template( tttp_params, tttp_sym->header, &tttp_sym->decl_position); + /* is_template_deducible_from deduces from the prototype instantiation + type, so record the defining-type-id there. */ + invented_alias_templ = template_supplement_for_symbol(invented_alias_sym) + ->il_template_entry; + invented_alias_templ->prototype_instantiation.type = defining_type; if (symbol_is(guide_set, sk_overloaded_function)) { is_list = TRUE; guide_sym = guide_set->variant.overloaded_function.symbols; @@ -46126,16 +46210,17 @@ a_symbol_ptr bound_template_template_argument( a_boolean *is_deducible_template) /* Return the template template argument bound to the type template template -parameter tttp_sym and set *is_deducible_template to indicate whether it is a +parameter tttp_sym (or tttp_sym itself when it is already a substituted +template argument) and set *is_deducible_template to indicate whether it is a deducible template when the binding is non-dependent, or NULL otherwise. */ { a_symbol_ptr result = NULL; *is_deducible_template = FALSE; - if (is_template_template_param_symbol(tttp_sym)) { - a_symbol_ptr bound = tttp_sym->variant.template_info - ->variant.class_template.argument_template; + if (tttp_sym != NULL) { + a_symbol_ptr bound = template_argument_if_template_template_param( + tttp_sym); if (bound != NULL && !bound->is_error && !is_template_template_param_symbol(bound)) { *is_deducible_template = is_deducible_template_symbol(bound); diff --git a/tests/tests/changes/.gh-173.rto/default.1.1.txt b/tests/tests/changes/.gh-173.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/changes/.gh-173.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/changes/gh-173.sft.cpp b/tests/tests/changes/gh-173.sft.cpp new file mode 100644 index 0000000000..0aa5a514a2 --- /dev/null +++ b/tests/tests/changes/gh-173.sft.cpp @@ -0,0 +1,8 @@ +//type:fp +//options:--c++20 + + template struct S { S(T); }; + template class TT> using A = decltype(TT(0)); + template class TT> + constexpr bool b = requires { typename A; }; + static_assert(b); // Previously an internal error, now okay. diff --git a/tests/tests/regressions/.gh-173.rto/default.1.1.txt b/tests/tests/regressions/.gh-173.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/regressions/.gh-173.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/regressions/.gh-173.rto/default.2.1.txt b/tests/tests/regressions/.gh-173.rto/default.2.1.txt new file mode 100644 index 0000000000..0f89a8995b --- /dev/null +++ b/tests/tests/regressions/.gh-173.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++20 --gn 150200 Test_name.c diff --git a/tests/tests/regressions/.gh-173.rto/default.3.1.txt b/tests/tests/regressions/.gh-173.rto/default.3.1.txt new file mode 100644 index 0000000000..a0f1c6652b --- /dev/null +++ b/tests/tests/regressions/.gh-173.rto/default.3.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=3 --c++20 --clang_version 230100 Test_name.c diff --git a/tests/tests/regressions/.gh-173.rto/default.4.1.txt b/tests/tests/regressions/.gh-173.rto/default.4.1.txt new file mode 100644 index 0000000000..8a9da70998 --- /dev/null +++ b/tests/tests/regressions/.gh-173.rto/default.4.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=4 --ms_c++20 --microsoft_version 1952 Test_name.c diff --git a/tests/tests/regressions/gh-173.sft.cpp b/tests/tests/regressions/gh-173.sft.cpp new file mode 100644 index 0000000000..f77f4ad57f --- /dev/null +++ b/tests/tests/regressions/gh-173.sft.cpp @@ -0,0 +1,686 @@ +//type:fp +//options:--c++20:--c++20 --gn 150200:--c++20 --clang_version 230100:--ms_c++20 --microsoft_version 1952 + +namespace variable_template_requirement +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace static_member_requirement +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + + template class C> + struct X { + static constexpr bool ok = requires { typename A; }; + }; + + static_assert(X::ok); + static_assert(!X::ok); +} + +namespace mismatched_parameter_positions +{ + template + struct S { S(T); }; + template + struct B { B(T, int); }; + + template class C> + using A = decltype(C(0)); + + template class C, template class D> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace chained_aliases +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + template class C> + using D = A; + + template class C> + constexpr bool ok = requires { typename D; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace variadic_call_alias +{ + template + T declval(); + + template + using A = decltype(declval()(declval()...)); + + struct S { void operator()(int); }; + struct B { void operator()(); }; + + template + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace mixed_class_and_alias_arguments +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + template + struct P {}; + + template class C> + using A = decltype(C(0)); + template class C> + using D = P>; + + template class C> + constexpr bool ok = requires { typename D; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace alias_parameter_beyond_enclosing_list +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace function_template_deduction +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + + template class C> + void f(A*); + + void g() { f(nullptr); } + + template class C> + constexpr bool ok = requires { f(nullptr); }; + + static_assert(ok); + static_assert(!ok); +} + +namespace mismatched_parameter_kinds +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0)); + + template class C, typename T> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace pointer_to_decltype +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(0))*; + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace qualified_decltype +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = const decltype(C(0)); + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace ordinary_alias_parameter_substitution +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(T())); + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); + + template + constexpr bool same = false; + template + constexpr bool same = true; + + static_assert(same, S>); +} + +namespace nested_template_template_binding +{ + template + struct B { B(T*); }; + + template class C> + using A = decltype((C(T()), C(T()))); + + template + constexpr bool p = requires { typename A; }; + + template + struct S { S(T) requires (!p); }; + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace nested_type_parameter_binding +{ + template + struct B { B(T, T); }; + + template class C> + using A = decltype((C(T()), C(T()))); + + template + constexpr bool p = requires { typename A; }; + + template + struct S { S(T) requires (!p); }; + + template + constexpr bool same = false; + template + constexpr bool same = true; + + template class C> + constexpr bool exact = same, C>; + + static_assert(exact); + static_assert(exact); +} + +namespace nested_nontype_parameter_binding +{ + template + struct K {}; + template + struct B { B(T, T); }; + + template class C> + using A = decltype((C(K()), C(K()))); + + template + constexpr bool p = requires { typename A; }; + + template + struct S { S(T) requires (!p); }; + + template class C> + constexpr bool ok = requires { typename A; }; + + template + constexpr bool same = false; + template + constexpr bool same = true; + + template class C> + constexpr bool exact = same, C>>; + + static_assert(ok<0, S>); + static_assert(!ok<0, B>); + static_assert(exact<0, S>); + static_assert(exact<2, S>); +} + +namespace enclosing_template_template_parameter +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(T())); + + template class D> + struct O { + template + static constexpr bool ok = requires { typename A; }; + }; + + static_assert(O::ok); + static_assert(!O::ok); +} + +namespace member_alias_of_enclosing_parameter +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(T())); + + template + constexpr bool same = false; + template + constexpr bool same = true; + + template class D> + struct O { + template + using M = A; + template + static constexpr bool ok = requires { typename M; }; + }; + + static_assert(same::M, S>); + static_assert(O::ok); + static_assert(!O::ok); +} + +namespace trailing_return_type +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + using A = decltype(C(T())); + + template class D, typename U> + auto f(int) -> A; + template class D, typename U> + void f(...); + + template + constexpr bool same = false; + template + constexpr bool same = true; + + static_assert(same(0)), S>); + static_assert(same(0)), void>); +} + +namespace constrained_member +{ + template + struct K { K(T); }; + + template class C> + using A = decltype(C(T())); + + template + struct S { void f() requires (sizeof(A) > 0); }; + + void g() { S().f(); } +} + +namespace constrained_guide_alias +{ + template + struct K { K(T); }; + + template class C> + using A = decltype(C(T())); + + template + struct S { S(T) requires (sizeof(A) > 0); }; + + S s(0); +} + +namespace constrained_guide_requirement +{ + template + struct K { K(T); }; + + template class C> + using A = decltype(C(T())); + + template + struct S { S(T) requires (sizeof(A) > 0); }; + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); +} + +namespace pack_expansion +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + template + struct P {}; + + template class... C> + constexpr bool ok = requires { typename P; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace pack_expansion_alias +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + template + struct P {}; + + template class... C> + using A = P; + + template class... C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace variadic_template_template_argument +{ + template + struct S { S(T); }; + + template class C> + using A = decltype(C(0)); + + using X = A; +} + +namespace variadic_template_template_parameter +{ + template + struct S { S(T); }; + + template class C> + using A = decltype(C(0)); + + template class D> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); +} + +namespace default_arguments_of_argument +{ + template + constexpr bool same = false; + template + constexpr bool same = true; + + template + struct S { S(T); }; + template + struct D { D(T); }; + template + struct V { V(T); }; + + template class C> + using A = decltype(C(0)); + + static_assert(same, S>); + static_assert(same, D>); + static_assert(same, V>); + + template class C> + auto f() { return C{1.0}; } + + static_assert(same()), S>); +} + +namespace pack_fold +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class... C> + constexpr bool ok = requires { (C(0), ...); }; + + static_assert(ok); + static_assert(!ok); + static_assert(!ok); +} + +namespace pack_expansion_with_trailing_defaults +{ + template + struct S { S(T); }; + template + struct D { D(T); }; + template + struct B { B(T*); }; + template + struct P {}; + + template class... C> + using A = P; + + template class... C> + constexpr bool ok = requires { typename A; }; + template class... C> + constexpr bool direct = requires { typename P; }; + template class... C> + constexpr bool fold = requires { (C(0), ...); }; + + static_assert(ok<>); + static_assert(ok); + static_assert(!ok); + static_assert(!ok); + static_assert(direct); + static_assert(!direct); + static_assert(!direct); + static_assert(fold); + static_assert(!fold); + static_assert(!fold); + + template + constexpr bool same = false; + template + constexpr bool same = true; + + static_assert(same, P, D>>); +} + +namespace dependent_member_trailing_defaults +{ + struct X { using type = int; }; + template + struct S { S(T); }; + template + struct D { D(T); }; + + template class C> + using A = decltype(C(T())); + + template class C> + constexpr bool ok = requires { typename A; }; + template class C> + constexpr bool direct = requires { C(T()); }; + + static_assert(ok); + static_assert(!ok); + static_assert(ok); + static_assert(!ok); + static_assert(direct); + static_assert(!direct); + static_assert(direct); + static_assert(!direct); + + template + constexpr bool same = false; + template + constexpr bool same = true; + + static_assert(same, S>); + static_assert(same, D>); +} + +namespace member_alias_of_specialization +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template + struct O { + template class C> + using M = decltype(C(X())); + }; + + template class C> + constexpr bool ok = requires { typename O::template M; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace nested_failing_alias_argument +{ + template + struct P {}; + template + struct B { B(T); }; + + template class C> + using A = P>; + + template class D> + constexpr bool p = requires { typename A; }; + + template + struct S { + S(T) requires (!p); + }; + + template class C> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); +} + +namespace mismatched_variadic_template_template_parameter +{ + template + struct P {}; + template + struct B { B(T); }; + template + struct S { S(T); }; + + template class C> + using A = P>; + + template class D, typename U> + constexpr bool ok = requires { typename A; }; + + static_assert(ok); + static_assert(!ok); +} + +namespace enclosing_class_parameter_in_member_alias +{ + template + struct S { S(T); }; + template + struct B { B(T*); }; + + template class C> + struct O { + template + using M = decltype(C(X())); + }; + + // GCC doesn't like this one + template + constexpr bool ok_s = requires { typename O::template M; }; + template + constexpr bool ok_b = requires { typename O::template M; }; + + static_assert(ok_s); + static_assert(!ok_b); +}