diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 64b64e1785706..d9972d697251a 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -240,7 +240,9 @@ combine_constraint_expressions (tree lhs, tree rhs) return rhs; if (!rhs) return lhs; - return finish_constraint_and_expr (input_location, lhs, rhs); + /* Use UNKNOWN_LOCATION so write_template_args can tell the difference + between this and a && the user wrote. */ + return finish_constraint_and_expr (UNKNOWN_LOCATION, lhs, rhs); } /* Extract the template-id from a concept check. For standard and variable @@ -1605,9 +1607,11 @@ finish_shorthand_constraint (tree decl, tree constr) check = ovl_make (tmpl); check = build_concept_check (check, arg, args, tf_warning_or_error); - /* Make the check a fold-expression if needed. */ + /* Make the check a fold-expression if needed. + Use UNKNOWN_LOCATION so write_template_args can tell the + difference between this and a fold the user wrote. */ if (apply_to_each_p && declared_pack_p) - check = finish_left_unary_fold_expr (DECL_SOURCE_LOCATION (decl), + check = finish_left_unary_fold_expr (UNKNOWN_LOCATION, check, TRUTH_ANDIF_EXPR); return check; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8809842da3fc2..b9adc1742cefa 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3799,6 +3799,12 @@ struct GTY(()) lang_decl { : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE)) #endif +/* True iff NODE represents the template args for a type-constraint, + in which case the first one represents the constrained type. + Currently only set during mangling. */ +#define TEMPLATE_ARGS_TYPE_CONSTRAINT_P(NODE) \ + TREE_PRIVATE (TREE_VEC_CHECK (NODE)) + /* The list of access checks that were deferred during parsing which need to be performed at template instantiation time. @@ -8509,6 +8515,7 @@ struct processing_constraint_expression_sentinel extern bool processing_constraint_expression_p (); extern tree unpack_concept_check (tree); +extern tree get_concept_check_template (tree); extern tree evaluate_concept_check (tree); extern bool constraints_satisfied_p (tree, tree = NULL_TREE); extern bool* lookup_subsumption_result (tree, tree); diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 5137305ed0753..8a44b11bab693 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -221,7 +221,7 @@ static void write_function_type (const tree); static void write_bare_function_type (const tree, const int, const tree); static void write_method_parms (tree, const int, const tree); static void write_class_enum_type (const tree); -static void write_template_args (tree); +static void write_template_args (tree, tree = NULL_TREE); static void write_expression (tree); static void write_template_arg_literal (const tree); static void write_template_arg (tree); @@ -842,6 +842,70 @@ mangle_return_type_p (tree decl) && maybe_template_info (decl)); } +/* ::= */ + +static void +write_constraint_expression (tree expr) +{ + write_expression (expr); +} + +/* Mangle a requires-clause following a template-head, if any. + + Q E */ + +static void +write_tparms_constraints (tree constraints) +{ + /* In a declaration with shorthand constraints in the template-head, followed + by a requires-clause, followed by shorthand constraints in the + function-parameter-list, the full constraints will be some && with the + parameter constraints on the RHS, around an && with the requires-clause on + the RHS. Find the requires-clause, if any. + + This logic relies on the && and ... from combine_constraint_expressions, + finish_shorthand_constraint, and convert_generic_types_to_packs having + UNKNOWN_LOCATION. If they need to have an actual location, we could move + to using a TREE_LANG_FLAG. */ + if (constraints && abi_check (19)) + { + tree probe = constraints; + while (probe + && !EXPR_LOCATION (probe) + && TREE_CODE (probe) == TRUTH_ANDIF_EXPR) + { + tree op1 = TREE_OPERAND (probe, 1); + probe = (EXPR_LOCATION (op1) ? op1 + : TREE_OPERAND (probe, 0)); + } + if (probe && EXPR_LOCATION (probe)) + { + write_char ('Q'); + write_constraint_expression (probe); + } + } +} + +/* ::= */ + +static void +write_type_constraint (tree cnst) +{ + if (!cnst) return; + + cnst = unpack_concept_check (cnst); + gcc_checking_assert (TREE_CODE (cnst) == TEMPLATE_ID_EXPR); + + tree concept_decl = get_concept_check_template (cnst); + write_name (concept_decl, 0); + tree args = TREE_OPERAND (cnst, 1); + if (TREE_VEC_LENGTH (args) > 1) + { + TEMPLATE_ARGS_TYPE_CONSTRAINT_P (args) = true; + write_template_args (args); + } +} + /* ::= ::= */ @@ -886,6 +950,14 @@ write_encoding (const tree decl) mangle_return_type_p (decl), d); + if (tree c = get_trailing_function_requirements (decl)) + if (abi_check (19)) + { + ++G.parm_depth; + write_char ('Q'); + write_constraint_expression (c); + --G.parm_depth; + } } } @@ -1037,7 +1109,13 @@ write_name (tree decl, const int ignore_local_scope) { /* Yes: use . */ write_unscoped_template_name (TI_TEMPLATE (info)); - write_template_args (TI_ARGS (info)); + /* Pass down the parms of a function template in case we need to + mangle them; we don't mangle the parms of a non-overloadable + template. */ + tree parms = (TREE_CODE (decl) == FUNCTION_DECL + ? DECL_TEMPLATE_PARMS (TI_TEMPLATE (info)) + : NULL_TREE); + write_template_args (TI_ARGS (info), parms); } else /* Everything else gets an . */ @@ -1722,10 +1800,136 @@ write_unnamed_type_name (const tree type) write_compact_number (discriminator); } +/* ABI issue #47: if a function template parameter is not "natural" for its + argument we must mangle the parameter. */ + +static bool +template_parm_natural_p (tree arg, tree parm) +{ + tree decl = TREE_VALUE (parm); + + /* A template parameter is "natural" if: */ + + if (template_parameter_pack_p (decl)) + { + tree args = ARGUMENT_PACK_ARGS (arg); + if (TREE_VEC_LENGTH (args) == 0) + { +#if 0 + /* the argument is an empty pack and the parameter is an + unconstrained template type parameter pack; */ + if (TREE_CODE (decl) != TYPE_DECL) + return false; +#else + /* Defer changing the mangling of C++11 code like + template int max(); + template int max(); */ + return true; +#endif + } + else + /* the argument is a non-empty pack and a non-pack variant of the + parameter would be natural for the first element of the pack; */ + arg = TREE_VEC_ELT (args, 0); + } + + /* the argument is a template and the parameter has the exact + same template head; */ + if (TREE_CODE (decl) == TEMPLATE_DECL) + return template_heads_equivalent_p (arg, decl); + + /* the argument is a type and the parameter is unconstrained; or */ + else if (TREE_CODE (decl) == TYPE_DECL) + return !TEMPLATE_PARM_CONSTRAINTS (parm); + + /* the argument is a non-type template argument and the declared parameter + type neither is instantiation dependent nor contains deduced types. */ + else if (TREE_CODE (decl) == PARM_DECL) + { +#if 0 + return !uses_template_parms (TREE_TYPE (decl)); +#else + /* Defer changing the mangling of C++98 code like + template .... */ + return !type_uses_auto (TREE_TYPE (decl)); +#endif + } + + gcc_unreachable (); +} + +/* Used for lambda template head and non-natural function template parameters. + + ::= Ty # template type parameter + ::= Tk # constrained type parameter + ::= Tn # template non-type parameter + ::= Tt * [Q # template parameter pack */ + +static void +write_template_param_decl (tree parm) +{ + tree decl = TREE_VALUE (parm); + + if (template_parameter_pack_p (decl)) + write_string ("Tp"); + + switch (TREE_CODE (decl)) + { + case PARM_DECL: + { + write_string ("Tn"); + + tree type = TREE_TYPE (decl); + if (tree c = (is_auto (type) + ? PLACEHOLDER_TYPE_CONSTRAINTS (type) + : NULL_TREE)) + { + if (AUTO_IS_DECLTYPE (type)) + write_string ("DK"); + else + write_string ("Dk"); + write_type_constraint (c); + } + else + write_type (type); + } + break; + + case TEMPLATE_DECL: + { + write_string ("Tt"); + tree parms = DECL_INNERMOST_TEMPLATE_PARMS (decl); + for (tree node : tree_vec_range (parms)) + write_template_param_decl (node); + write_char ('E'); + } + break; + + case TYPE_DECL: + if (tree c = TEMPLATE_PARM_CONSTRAINTS (parm)) + { + if (TREE_CODE (c) == UNARY_LEFT_FOLD_EXPR) + { + c = FOLD_EXPR_PACK (c); + c = PACK_EXPANSION_PATTERN (c); + } + if (TREE_CODE (decl) == TYPE_DECL) + { + write_string ("Tk"); + write_type_constraint (c); + } + } + else + write_string ("Ty"); + break; + + default: + gcc_unreachable (); + } +} + // A template head, for templated lambdas. -// ::= Tp* Ty -// Tp* Tn -// Tp* Tt E // New in ABI=18. Returns true iff we emitted anything -- used for ABI // version warning. @@ -1735,50 +1939,26 @@ write_closure_template_head (tree tmpl) bool any = false; // We only need one level of template parms - tree inner = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + tree parms = DECL_TEMPLATE_PARMS (tmpl); + tree inner = INNERMOST_TEMPLATE_PARMS (parms); for (int ix = 0, len = TREE_VEC_LENGTH (inner); ix != len; ix++) { tree parm = TREE_VEC_ELT (inner, ix); if (parm == error_mark_node) continue; - parm = TREE_VALUE (parm); - if (DECL_IMPLICIT_TEMPLATE_PARM_P (parm)) + if (DECL_IMPLICIT_TEMPLATE_PARM_P (TREE_VALUE (parm))) // A synthetic parm, we're done. break; any = true; if (abi_version_at_least (18)) - { - if (TREE_CODE (parm) == PARM_DECL - ? TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)) - : TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm))) - write_string ("Tp"); - - switch (TREE_CODE (parm)) - { - default: - gcc_unreachable (); - - case TYPE_DECL: - write_string ("Ty"); - break; - - case PARM_DECL: - write_string ("Tn"); - write_type (TREE_TYPE (parm)); - break; - - case TEMPLATE_DECL: - write_string ("Tt"); - write_closure_template_head (parm); - write_string ("E"); - break; - } - } + write_template_param_decl (parm); } + write_tparms_constraints (TEMPLATE_PARMS_CONSTRAINTS (parms)); + return any; } @@ -2893,13 +3073,84 @@ write_class_enum_type (const tree type) write_name (TYPE_NAME (type), /*ignore_local_scope=*/0); } +/* Mangle a requirement REQ in a requires-expression. */ + +static void +write_requirement (tree req) +{ + tree op = TREE_OPERAND (req, 0); + + switch (tree_code code = TREE_CODE (req)) + { + /* # simple-requirement or compound-requirement + ::= X [ N ] [ R ] */ + case SIMPLE_REQ: + case COMPOUND_REQ: + write_char ('X'); + write_expression (op); + if (code == SIMPLE_REQ) + break; + if (COMPOUND_REQ_NOEXCEPT_P (req)) + write_char ('N'); + if (tree constr = TREE_OPERAND (req, 1)) + { + write_char ('R'); + write_type_constraint (PLACEHOLDER_TYPE_CONSTRAINTS (constr)); + } + break; + + /* ::= T # type-requirement */ + case TYPE_REQ: + write_char ('T'); + write_type (op); + break; + + /* ::= Q # nested-requirement */ + case NESTED_REQ: + write_char ('Q'); + write_constraint_expression (op); + break; + + default: + gcc_unreachable (); + } +} + +/* # requires { ... } + ::= rq + E + # requires (...) { ... } + ::= rQ _ + E */ + +static void +write_requires_expr (tree expr) +{ + tree parms = REQUIRES_EXPR_PARMS (expr); + if (parms) + { + write_string ("rQ"); + ++G.parm_depth; + for (; parms; parms = DECL_CHAIN (parms)) + write_type (cv_unqualified (TREE_TYPE (parms))); + --G.parm_depth; + write_char ('_'); + } + else + write_string ("rq"); + + for (tree reqs = REQUIRES_EXPR_REQS (expr); reqs; + reqs = TREE_CHAIN (reqs)) + write_requirement (TREE_VALUE (reqs)); + + write_char ('E'); +} + /* Non-terminal . ARGS is a TREE_VEC of template arguments. - ::= I * E */ + ::= I * [Q ] E */ static void -write_template_args (tree args) +write_template_args (tree args, tree parms /*= NULL_TREE*/) { int i; int length = 0; @@ -2911,6 +3162,13 @@ write_template_args (tree args) if (args) length = TREE_VEC_LENGTH (args); + tree constraints = NULL_TREE; + if (parms) + { + constraints = TEMPLATE_PARMS_CONSTRAINTS (parms); + parms = INNERMOST_TEMPLATE_PARMS (parms); + } + if (args && length && TREE_CODE (TREE_VEC_ELT (args, 0)) == TREE_VEC) { /* We have nested template args. We want the innermost template @@ -2918,8 +3176,38 @@ write_template_args (tree args) args = TREE_VEC_ELT (args, length - 1); length = TREE_VEC_LENGTH (args); } - for (i = 0; i < length; ++i) - write_template_arg (TREE_VEC_ELT (args, i)); + if (TEMPLATE_ARGS_TYPE_CONSTRAINT_P (args)) + /* Skip the constrained type. */ + i = 1; + else + i = 0; + bool implicit_parm_scope = false; + for (; i < length; ++i) + { + tree arg = TREE_VEC_ELT (args, i); + if (parms) + { + tree parm = TREE_VEC_ELT (parms, i); + tree decl = TREE_VALUE (parm); + if (DECL_IMPLICIT_TEMPLATE_PARM_P (decl) + && !implicit_parm_scope) + { + /* The rest of the template parameters are based on generic + function parameters, so any expressions in their + type-constraints are in parameter scope. */ + implicit_parm_scope = true; + ++G.parm_depth; + } + if (!template_parm_natural_p (arg, parm) + && abi_check (19)) + write_template_param_decl (parm); + } + write_template_arg (arg); + } + if (implicit_parm_scope) + --G.parm_depth; + + write_tparms_constraints (constraints); write_char ('E'); } @@ -3107,6 +3395,7 @@ write_expression (tree expr) write_char ('f'); if (delta != 0) { + gcc_checking_assert (delta > 0); if (abi_check (5)) { /* Let L be the number of function prototype scopes from the @@ -3431,6 +3720,8 @@ write_expression (tree expr) write_type (LAMBDA_EXPR_CLOSURE (expr)); write_char ('E'); } + else if (code == REQUIRES_EXPR) + write_requires_expr (expr); else if (dependent_name (expr)) { tree name = dependent_name (expr); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c54b2560bad0a..d12d57686f2a9 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -30951,7 +30951,9 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx) { tree id = unpack_concept_check (constr); TREE_VEC_ELT (TREE_OPERAND (id, 1), 0) = t; - location_t loc = DECL_SOURCE_LOCATION (TYPE_NAME (t)); + /* Use UNKNOWN_LOCATION so write_template_args can tell the + difference between this and a fold the user wrote. */ + location_t loc = UNKNOWN_LOCATION; tree fold = finish_left_unary_fold_expr (loc, constr, TRUTH_ANDIF_EXPR); TEMPLATE_PARM_CONSTRAINTS (node) = fold; diff --git a/gcc/testsuite/g++.dg/abi/mangle-concepts1.C b/gcc/testsuite/g++.dg/abi/mangle-concepts1.C new file mode 100644 index 0000000000000..eac520cef3c07 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle-concepts1.C @@ -0,0 +1,88 @@ +// { dg-do compile { target c++20 } } + +template concept C = true; +template concept C2 = true; +template concept D = true; +template concept E = true; +template concept F = true; +template using Ref = T&; + +// { dg-final { scan-assembler "_Z1fIiQ1CIT_EEvv" } } +template requires C void f() {} +template void f(); + +// { dg-final { scan-assembler "_Z2f2ITk1CiEvv" } } +template void f2() {} +template void f2(); + +// { dg-final { scan-assembler "_Z2f3IiEvvQ1CIT_E" } } +template void f3() requires C {} +template void f3(); + +// { dg-final { scan-assembler "_Z2f4ITk1CiEvT_" } } +void f4(C auto c) {} +template void f4(int); + +// ??? The constraints end up out of order in the mangled name, may +// need to change the equivalence rule. +// { dg-final { scan-assembler "_Z2f5ITk1CicTk1EfTk1FsQ1DIT0_EEvT1_T2_" } } +template requires D void f5(E auto c, F auto f) {} +template void f5(float,short); + +// { dg-final { scan-assembler "_Z2f6ITk2C2IiEsEvv" } } +template T> void f6() {} +template void f6(); + +// { dg-final { scan-assembler "_ZN1AIiE1fEvQ1CIT_E" } } +template struct A { + void f() requires C { }; +}; +template struct A; + +// { dg-final { scan-assembler "_Z1gIiQrqXcvT__ETRS0_Q1CIS0_EXpscvS0__ENR1CEEvv" } } +template +requires requires { T(); + typename Ref; + requires C; + { +T() } noexcept -> C; +} +void g() {} +template void g(); + +// { dg-final { scan-assembler "_Z1hIiQrQT__Xpsfp_EEvv" } } +template +requires requires (T t) { +t; } +void h() {} +template void h(); + +// { dg-final { scan-assembler "_Z3fn1IiEvT_QrQS0__XpsfL0p_Xpsfp_E" } } +template +void fn1(T t1) + requires requires (T t2) { +t1; +t2; } +{} +template void fn1(int); + +// { dg-final { scan-assembler "_Z3fn3IiTk2C2IDtfL0p_EEiEvT_T0_" } } +template void fn3(T t, C2 auto) {} +template void fn3(int, int); + +// { dg-final { scan-assembler "_Z3fn4IiiEvT_T0_Q2C2IS1_FDTcl3fn3fL0p_fp_EES0_EE" } } +template void fn4(T t, U u) + requires C2 decltype(fn3(t, u))> {} +template void fn4(int, int); + +// { dg-final { scan-assembler "_Z3fn5ITpTk1CJicfEEvDpT_" } } +template void fn5(T...) { } +template void fn5(int,char,float); + +// { dg-final { scan-assembler "_ZN2A2IiE1BIiE1fIiiEEvvQ2C2IT_TL1_0_E" } } +template struct A2 { + template struct B { + template void f() requires C2 {} + }; +}; +template void A2::B::f(); + +template void f7() {} +// { dg-final { scan-assembler "_Z2f7ITnDk1CLi5EEvv" } } +template void f7<5>(); diff --git a/gcc/testsuite/g++.dg/abi/mangle-ttp1.C b/gcc/testsuite/g++.dg/abi/mangle-ttp1.C new file mode 100644 index 0000000000000..2f5878fc3fe10 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle-ttp1.C @@ -0,0 +1,27 @@ +// ABI #47 "natural" template parameter mangling +// { dg-do compile { target c++17 } } + +template