aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/mangle.cc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2023-09-25 10:15:02 +0100
committerJason Merrill <jason@redhat.com>2023-12-01 16:08:25 -0500
commitc3f281a0c1ca50e4df5049923aa2f5d1c3c39ff6 (patch)
tree82e7a2bedea7119330320edbaff2c8ba3f642cd9 /gcc/cp/mangle.cc
parentc6bb413eeb9d13412e8101e3029099d7fd746708 (diff)
downloadgcc-c3f281a0c1ca50e4df5049923aa2f5d1c3c39ff6.zip
gcc-c3f281a0c1ca50e4df5049923aa2f5d1c3c39ff6.tar.gz
gcc-c3f281a0c1ca50e4df5049923aa2f5d1c3c39ff6.tar.bz2
c++: mangle function template constraints
Per https://github.com/itanium-cxx-abi/cxx-abi/issues/24 and https://github.com/itanium-cxx-abi/cxx-abi/pull/166 We need to mangle constraints to be able to distinguish between function templates that only differ in constraints. From the latter link, we want to use the template parameter mangling previously specified for lambdas to also make explicit the form of a template parameter where the argument is not a "natural" fit for it, such as when the parameter is constrained or deduced. I'm concerned about how the latter link changes the mangling for some C++98 and C++11 patterns, so I've limited template_parm_natural_p to avoid two cases found by running the testsuite with -Wabi forced on: template <class T, T V> T f() { return V; } int main() { return f<int,42>(); } template <int i> int max() { return i; } template <int i, int j, int... rest> int max() { int sub = max<j, rest...>(); return i > sub ? i : sub; } int main() { return max<1,2,3>(); } A third C++11 pattern is changed by this patch: template <template <typename...> class TT, typename... Ts> TT<Ts...> f(); template <typename> struct A { }; int main() { f<A,int>(); } I aim to resolve these with the ABI committee before GCC 14.1. We also need to resolve https://github.com/itanium-cxx-abi/cxx-abi/issues/38 (mangling references to dependent template-ids where the name is fully resolved) as references to concepts in std:: will consistently run into this area. This is why mangle-concepts1.C only refers to concepts in the global namespace so far. The library changes are to avoid trying to mangle builtins, which fails. Demangler support and test coverage is not complete yet. gcc/cp/ChangeLog: * cp-tree.h (TEMPLATE_ARGS_TYPE_CONSTRAINT_P): New. (get_concept_check_template): Declare. * constraint.cc (combine_constraint_expressions) (finish_shorthand_constraint): Use UNKNOWN_LOCATION. * pt.cc (convert_generic_types_to_packs): Likewise. * mangle.cc (write_constraint_expression) (write_tparms_constraints, write_type_constraint) (template_parm_natural_p, write_requirement) (write_requires_expr): New. (write_encoding): Mangle trailing requires-clause. (write_name): Pass parms to write_template_args. (write_template_param_decl): Factor out from... (write_closure_template_head): ...here. (write_template_args): Mangle non-natural parms and requires-clause. (write_expression): Handle REQUIRES_EXPR. include/ChangeLog: * demangle.h (enum demangle_component_type): Add DEMANGLE_COMPONENT_CONSTRAINTS. libiberty/ChangeLog: * cp-demangle.c (d_make_comp): Handle DEMANGLE_COMPONENT_CONSTRAINTS. (d_count_templates_scopes): Likewise. (d_print_comp_inner): Likewise. (d_maybe_constraints): New. (d_encoding, d_template_args_1): Call it. (d_parmlist): Handle 'Q'. * testsuite/demangle-expected: Add some constraint tests. libstdc++-v3/ChangeLog: * include/std/bit: Avoid builtins in requires-clauses. * include/std/variant: Likewise. gcc/testsuite/ChangeLog: * g++.dg/abi/mangle10.C: Disable compat aliases. * g++.dg/abi/mangle52.C: Specify ABI 18. * g++.dg/cpp2a/class-deduction-alias3.C * g++.dg/cpp2a/class-deduction-alias8.C: Avoid builtins in requires-clauses. * g++.dg/abi/mangle-concepts1.C: New test. * g++.dg/abi/mangle-ttp1.C: New test.
Diffstat (limited to 'gcc/cp/mangle.cc')
-rw-r--r--gcc/cp/mangle.cc369
1 files changed, 330 insertions, 39 deletions
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 5137305..8a44b11 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));
}
+/* <constraint-expression> ::= <expression> */
+
+static void
+write_constraint_expression (tree expr)
+{
+ write_expression (expr);
+}
+
+/* Mangle a requires-clause following a template-head, if any.
+
+ Q <constraint_expression> 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);
+ }
+ }
+}
+
+/* <type-constraint> ::= <name> */
+
+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);
+ }
+}
+
/* <encoding> ::= <function name> <bare-function-type>
::= <data name> */
@@ -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 <unscoped-template-name>. */
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 <unqualified-name>. */
@@ -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 i> int max();
+ template <int i, int j, int... rest> 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 <class T, T V> .... */
+ return !type_uses_auto (TREE_TYPE (decl));
+#endif
+ }
+
+ gcc_unreachable ();
+}
+
+/* Used for lambda template head and non-natural function template parameters.
+
+ <template-param-decl> ::= Ty # template type parameter
+ ::= Tk <type-constraint> # constrained type parameter
+ ::= Tn <type> # template non-type parameter
+ ::= Tt <template-param-decl>* [Q <constraint-expression] E # ttp
+ ::= Tp <non-pack template-param-decl> # 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.
-// <template-head> ::= Tp* Ty
-// Tp* Tn <type>
-// Tp* Tt <template-head> 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
+ <requirement> ::= X <expression> [ N ] [ R <type-constraint> ] */
+ 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;
+
+ /* <requirement> ::= T <type> # type-requirement */
+ case TYPE_REQ:
+ write_char ('T');
+ write_type (op);
+ break;
+
+ /* <requirement> ::= Q <constraint-expression> # nested-requirement */
+ case NESTED_REQ:
+ write_char ('Q');
+ write_constraint_expression (op);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* # requires { ... }
+ <expression> ::= rq <requirement>+ E
+ # requires (...) { ... }
+ <expression> ::= rQ <bare-function-type> _ <requirement>+ 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 <template-args>. ARGS is a TREE_VEC of template
arguments.
- <template-args> ::= I <template-arg>* E */
+ <template-args> ::= I <template-arg>* [Q <constraint-expr>] 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);