@@ -3800,6 +3800,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);
@@ -466,6 +466,8 @@ enum demangle_component_type
DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM,
DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM,
+ DEMANGLE_COMPONENT_CONSTRAINTS,
+
/* A builtin type with argument. This holds the builtin type
information. */
DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE
@@ -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;
@@ -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);
@@ -30945,7 +30945,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;
new file mode 100644
@@ -0,0 +1,88 @@
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C = true;
+template <class T, class U> concept C2 = true;
+template <class T> concept D = true;
+template <class T> concept E = true;
+template <class T> concept F = true;
+template <class T> using Ref = T&;
+
+// { dg-final { scan-assembler "_Z1fIiQ1CIT_EEvv" } }
+template <class T> requires C<T> void f() {}
+template void f<int>();
+
+// { dg-final { scan-assembler "_Z2f2ITk1CiEvv" } }
+template <C T> void f2() {}
+template void f2<int>();
+
+// { dg-final { scan-assembler "_Z2f3IiEvvQ1CIT_E" } }
+template <class T> void f3() requires C<T> {}
+template void f3<int>();
+
+// { 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 <C T, class U> requires D<U> void f5(E auto c, F auto f) {}
+template void f5<int,char>(float,short);
+
+// { dg-final { scan-assembler "_Z2f6ITk2C2IiEsEvv" } }
+template <C2<int> T> void f6() {}
+template void f6<short>();
+
+// { dg-final { scan-assembler "_ZN1AIiE1fEvQ1CIT_E" } }
+template <class T> struct A {
+ void f() requires C<T> { };
+};
+template struct A<int>;
+
+// { dg-final { scan-assembler "_Z1gIiQrqXcvT__ETRS0_Q1CIS0_EXpscvS0__ENR1CEEvv" } }
+template <class T>
+requires requires { T();
+ typename Ref<T>;
+ requires C<T>;
+ { +T() } noexcept -> C;
+}
+void g() {}
+template void g<int>();
+
+// { dg-final { scan-assembler "_Z1hIiQrQT__Xpsfp_EEvv" } }
+template <class T>
+requires requires (T t) { +t; }
+void h() {}
+template void h<int>();
+
+// { dg-final { scan-assembler "_Z3fn1IiEvT_QrQS0__XpsfL0p_Xpsfp_E" } }
+template <class T>
+void fn1(T t1)
+ requires requires (T t2) { +t1; +t2; }
+{}
+template void fn1<int>(int);
+
+// { dg-final { scan-assembler "_Z3fn3IiTk2C2IDtfL0p_EEiEvT_T0_" } }
+template<typename T> void fn3(T t, C2<decltype(t)> auto) {}
+template void fn3(int, int);
+
+// { dg-final { scan-assembler "_Z3fn4IiiEvT_T0_Q2C2IS1_FDTcl3fn3fL0p_fp_EES0_EE" } }
+template<typename T, typename U> void fn4(T t, U u)
+ requires C2<U, auto (T u) -> decltype(fn3(t, u))> {}
+template void fn4(int, int);
+
+// { dg-final { scan-assembler "_Z3fn5ITpTk1CJicfEEvDpT_" } }
+template<C... T> void fn5(T...) { }
+template void fn5(int,char,float);
+
+// { dg-final { scan-assembler "_ZN2A2IiE1BIiE1fIiiEEvvQ2C2IT_TL1_0_E" } }
+template <class T> struct A2 {
+ template <class X> struct B {
+ template <class U, class V> void f() requires C2<T,V> {}
+ };
+};
+template void A2<int>::B<int>::f<int,int>();
+
+template<C auto N> void f7() {}
+// { dg-final { scan-assembler "_Z2f7ITnDk1CLi5EEvv" } }
+template void f7<5>();
new file mode 100644
@@ -0,0 +1,27 @@
+// ABI #47 "natural" template parameter mangling
+// { dg-do compile { target c++17 } }
+
+template <template <class...> class TT> class A { };
+template <int... T> class B { };
+
+template <auto... T>
+void f(B<T...> b);
+
+template <template <auto...> class TT>
+void g(TT<42>);
+
+template <template <int...> class TT>
+void h(TT<42>);
+
+template <class T> struct C {
+ template <template <T...> class TT> static void j(TT<42>);
+};
+
+int main()
+{
+ B<42> b;
+ f(b); // { dg-final { scan-assembler "_Z1fITpTnDaJLi42EEEv1BIJXspT_EEE" } }
+ g(b); // { dg-final { scan-assembler "_Z1gITtTpTnDaE1BEvT_IJLi42EEE" } }
+ h(b); // { dg-final { scan-assembler "_Z1hI1BEvT_IJLi42EEE" } }
+ C<int>::j(b); // { dg-final { scan-assembler "_ZN1CIiE1jI1BEEvT_IJLi42EEE" } }
+}
@@ -1,4 +1,4 @@
-// { dg-options "-fabi-version=0" }
+// { dg-options "-fabi-version=0 -fabi-compat-version=0" }
template <template <typename> class Q>
void f (typename Q<int>::X) {}
@@ -1,4 +1,4 @@
-// { dg-options "-fabi-version=0 -Wabi=2" }
+// { dg-options "-fabi-version=18 -Wabi=2" }
template <unsigned int> struct helper {};
// { dg-final { scan-assembler "\n_?_Z6check1IiEvP6helperIXszscT_Li1EEE\[: \t\n\]" } }
@@ -1,8 +1,11 @@
// PR c++/95486
// { dg-do compile { target c++20 } }
+template <class T>
+concept Int = __is_same (T, int);
+
template<class T, class U>
-struct X { X(U) requires __is_same(U, int) {} };
+struct X { X(U) requires Int<U> {} };
template<class U>
using Y = X<void, U>;
@@ -1,8 +1,11 @@
// PR c++/95486
// { dg-do compile { target c++20 } }
+template <class T>
+concept Int = __is_same (T, int);
+
template<class T, class U>
-struct X { X(U) requires __is_same(U, int) {} };
+struct X { X(U) requires Int<U> {} };
template<class U>
X(U) -> X<char, U>;
@@ -993,6 +993,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_VECTOR_TYPE:
case DEMANGLE_COMPONENT_CLONE:
case DEMANGLE_COMPONENT_MODULE_ENTITY:
+ case DEMANGLE_COMPONENT_CONSTRAINTS:
if (left == NULL || right == NULL)
return NULL;
break;
@@ -1345,6 +1346,22 @@ is_ctor_dtor_or_conversion (struct demangle_component *dc)
}
}
+/* [ Q <constraint-expression> ] */
+
+static struct demangle_component *
+d_maybe_constraints (struct d_info *di, struct demangle_component *dc)
+{
+ if (d_peek_char (di) == 'Q')
+ {
+ d_advance (di, 1);
+ struct demangle_component *expr = d_expression (di);
+ if (expr == NULL)
+ return NULL;
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_CONSTRAINTS, dc, expr);
+ }
+ return dc;
+}
+
/* <encoding> ::= <(function) name> <bare-function-type>
::= <(data) name>
::= <special-name>
@@ -1398,21 +1415,21 @@ d_encoding (struct d_info *di, int top_level)
struct demangle_component *ftype;
ftype = d_bare_function_type (di, has_return_type (dc));
- if (ftype)
- {
- /* If this is a non-top-level local-name, clear the
- return type, so it doesn't confuse the user by
- being confused with the return type of whaever
- this is nested within. */
- if (!top_level && dc->type == DEMANGLE_COMPONENT_LOCAL_NAME
- && ftype->type == DEMANGLE_COMPONENT_FUNCTION_TYPE)
- d_left (ftype) = NULL;
+ if (!ftype)
+ return NULL;
- dc = d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME,
- dc, ftype);
- }
- else
- dc = NULL;
+ /* If this is a non-top-level local-name, clear the
+ return type, so it doesn't confuse the user by
+ being confused with the return type of whaever
+ this is nested within. */
+ if (!top_level && dc->type == DEMANGLE_COMPONENT_LOCAL_NAME
+ && ftype->type == DEMANGLE_COMPONENT_FUNCTION_TYPE)
+ d_left (ftype) = NULL;
+
+ ftype = d_maybe_constraints (di, ftype);
+
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME,
+ dc, ftype);
}
}
}
@@ -3023,7 +3040,7 @@ d_parmlist (struct d_info *di)
struct demangle_component *type;
char peek = d_peek_char (di);
- if (peek == '\0' || peek == 'E' || peek == '.')
+ if (peek == '\0' || peek == 'E' || peek == '.' || peek == 'Q')
break;
if ((peek == 'R' || peek == 'O')
&& d_peek_next_char (di) == 'E')
@@ -3259,7 +3276,7 @@ d_template_args (struct d_info *di)
return d_template_args_1 (di);
}
-/* <template-arg>* E */
+/* <template-arg>* [Q <constraint-expression>] E */
static struct demangle_component *
d_template_args_1 (struct d_info *di)
@@ -3295,13 +3312,17 @@ d_template_args_1 (struct d_info *di)
return NULL;
pal = &d_right (*pal);
- if (d_peek_char (di) == 'E')
- {
- d_advance (di, 1);
- break;
- }
+ char peek = d_peek_char (di);
+ if (peek == 'E' || peek == 'Q')
+ break;
}
+ al = d_maybe_constraints (di, al);
+
+ if (d_peek_char (di) != 'E')
+ return NULL;
+ d_advance (di, 1);
+
di->last_name = hold_last_name;
return al;
@@ -4442,6 +4463,7 @@ d_count_templates_scopes (struct d_print_info *dpi,
case DEMANGLE_COMPONENT_PACK_EXPANSION:
case DEMANGLE_COMPONENT_TAGGED_NAME:
case DEMANGLE_COMPONENT_CLONE:
+ case DEMANGLE_COMPONENT_CONSTRAINTS:
recurse_left_right:
/* PR 89394 - Check for too much recursion. */
if (dpi->recursion > DEMANGLE_RECURSION_LIMIT)
@@ -5201,6 +5223,22 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
dpt.next = dpi->templates;
dpi->templates = &dpt;
dpt.template_decl = typed_name;
+
+ /* Constraints are mangled as part of the template argument list,
+ so they wrap the _TEMPLATE_ARGLIST. But
+ d_lookup_template_argument expects the RHS of _TEMPLATE to be
+ the _ARGLIST, and constraints need to refer to these args. So
+ move the _CONSTRAINTS out of the _TEMPLATE and onto the type.
+ This will result in them being printed after the () like a
+ trailing requires-clause, but that seems like our best option
+ given that we aren't printing a template-head. */
+ struct demangle_component *tnr = d_right (typed_name);
+ if (tnr->type == DEMANGLE_COMPONENT_CONSTRAINTS)
+ {
+ d_right (typed_name) = d_left (tnr);
+ d_left (tnr) = d_right (dc);
+ d_right (dc) = tnr;
+ }
}
d_print_comp (dpi, options, d_right (dc));
@@ -6248,6 +6286,12 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
d_append_string (dpi, "...");
return;
+ case DEMANGLE_COMPONENT_CONSTRAINTS:
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_string (dpi, " requires ");
+ d_print_comp (dpi, options, d_right (dc));
+ return;
+
default:
d_print_error (dpi);
return;
@@ -1692,3 +1692,11 @@ X<float>::operator Z<int><int>()::y
_ZN1SILi1EEF3barIiEEiR4Base
int S<1>::bar[friend]<int>(Base&)
+
+# requires on template-head
+_Z1fIiQ1CIT_EEvv
+void f<int>() requires C<int>
+
+# requires after ()
+_Z1fIiEvvQ1CIT_E
+void f<int>() requires C<int>
@@ -88,7 +88,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bit_cast(const _From& __from) noexcept
#ifdef __cpp_concepts
requires (sizeof(_To) == sizeof(_From))
- && __is_trivially_copyable(_To) && __is_trivially_copyable(_From)
+ && is_trivially_copyable_v<_To> && is_trivially_copyable_v<_From>
#endif
{
return __builtin_bit_cast(_To, __from);
@@ -426,8 +426,8 @@ namespace __variant
~_Variadic_union() = default;
constexpr ~_Variadic_union()
- requires (!__has_trivial_destructor(_First))
- || (!__has_trivial_destructor(_Variadic_union<_Rest...>))
+ requires (!is_trivially_destructible_v<_First>)
+ || (!is_trivially_destructible_v<_Variadic_union<_Rest...>>)
{ }
#endif