public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH RFC] c++: mangle function template constraints
@ 2023-11-20  2:55 Jason Merrill
  2023-11-22 14:50 ` Jonathan Wakely
  0 siblings, 1 reply; 6+ messages in thread
From: Jason Merrill @ 2023-11-20  2:55 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++

Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
push this?

-- 8< --

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.
---
 gcc/cp/cp-tree.h                              |   7 +
 include/demangle.h                            |   2 +
 gcc/cp/constraint.cc                          |  10 +-
 gcc/cp/mangle.cc                              | 369 ++++++++++++++++--
 gcc/cp/pt.cc                                  |   4 +-
 gcc/testsuite/g++.dg/abi/mangle-concepts1.C   |  88 +++++
 gcc/testsuite/g++.dg/abi/mangle-ttp1.C        |  27 ++
 gcc/testsuite/g++.dg/abi/mangle10.C           |   2 +-
 gcc/testsuite/g++.dg/abi/mangle52.C           |   2 +-
 .../g++.dg/cpp2a/class-deduction-alias3.C     |   5 +-
 .../g++.dg/cpp2a/class-deduction-alias8.C     |   5 +-
 libiberty/cp-demangle.c                       |  86 +++-
 libiberty/testsuite/demangle-expected         |   8 +
 libstdc++-v3/include/std/bit                  |   2 +-
 libstdc++-v3/include/std/variant              |   4 +-
 15 files changed, 550 insertions(+), 71 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/mangle-concepts1.C
 create mode 100644 gcc/testsuite/g++.dg/abi/mangle-ttp1.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7b0b7c6a17e..138894dac98 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/include/demangle.h b/include/demangle.h
index f062d7731c6..0d3e2bda21e 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -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
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 64b64e17857..d9972d69725 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/mangle.cc b/gcc/cp/mangle.cc
index 5137305ed07..8a44b11bab6 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);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ed681afb5d4..2c43fd603e4 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -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;
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 00000000000..eac520cef3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-concepts1.C
@@ -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>();
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 00000000000..2f5878fc3fe
--- /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 <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" } }
+}
diff --git a/gcc/testsuite/g++.dg/abi/mangle10.C b/gcc/testsuite/g++.dg/abi/mangle10.C
index d5782ba6dfc..fcbb815be8f 100644
--- a/gcc/testsuite/g++.dg/abi/mangle10.C
+++ b/gcc/testsuite/g++.dg/abi/mangle10.C
@@ -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) {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle52.C b/gcc/testsuite/g++.dg/abi/mangle52.C
index 0b9a72fd217..1e7eca042bf 100644
--- a/gcc/testsuite/g++.dg/abi/mangle52.C
+++ b/gcc/testsuite/g++.dg/abi/mangle52.C
@@ -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\]" } }
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias3.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias3.C
index 318d4c942ce..b43a8c854eb 100644
--- a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias3.C
@@ -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>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias8.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias8.C
index ec005956fa6..9de0a7217a4 100644
--- a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias8.C
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias8.C
@@ -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>;
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 2ce984f85cf..151f2ebfa52 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -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;
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index 01ca22278cd..0997e96ea43 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -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>
diff --git a/libstdc++-v3/include/std/bit b/libstdc++-v3/include/std/bit
index dce61b440c5..07b8988c152 100644
--- a/libstdc++-v3/include/std/bit
+++ b/libstdc++-v3/include/std/bit
@@ -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);
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 7f24e760bb1..534cc31d6f7 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -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
 

base-commit: e85c596ae2d1e5f5b769b5af4c0a8e7d055e40d7
-- 
2.39.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] c++: mangle function template constraints
  2023-11-20  2:55 [PATCH RFC] c++: mangle function template constraints Jason Merrill
@ 2023-11-22 14:50 ` Jonathan Wakely
  2023-11-22 14:54   ` Jonathan Wakely
  2023-12-05 17:05   ` Jonathan Wakely
  0 siblings, 2 replies; 6+ messages in thread
From: Jonathan Wakely @ 2023-11-22 14:50 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, libstdc++

On Mon, 20 Nov 2023 at 02:56, Jason Merrill wrote:
>
> Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
> push this?

The library parts are OK.

The variable template is_trivially_copyable_v just uses
__is_trivially_copyable so should be just as efficient, and the change
to <bit> is fine.

The variable template is_trivially_destructible_v instantiates the
is_trivially_destructible type trait, which instantiates
__is_destructible_safe and __is_destructible_impl, which is probably
why we used the built-in directly in <variant>. But that's an
acceptable overhead to avoid using the built-in in a mangled context,
and it would be good to optimize the variable template anyway, as a
separate change.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] c++: mangle function template constraints
  2023-11-22 14:50 ` Jonathan Wakely
@ 2023-11-22 14:54   ` Jonathan Wakely
  2023-12-05 17:05   ` Jonathan Wakely
  1 sibling, 0 replies; 6+ messages in thread
From: Jonathan Wakely @ 2023-11-22 14:54 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, libstdc++

On Wed, 22 Nov 2023 at 14:50, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Mon, 20 Nov 2023 at 02:56, Jason Merrill wrote:
> >
> > Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
> > push this?
>
> The library parts are OK.
>
> The variable template is_trivially_copyable_v just uses
> __is_trivially_copyable so should be just as efficient, and the change
> to <bit> is fine.
>
> The variable template is_trivially_destructible_v instantiates the
> is_trivially_destructible type trait, which instantiates
> __is_destructible_safe and __is_destructible_impl, which is probably
> why we used the built-in directly in <variant>. But that's an
> acceptable overhead to avoid using the built-in in a mangled context,
> and it would be good to optimize the variable template anyway, as a
> separate change.

For C++20 we could do:

#if __cpp_concepts
template <typename _Tp>
  inline constexpr bool is_trivially_destructible_v = false;
template <typename _Tp> requires (_Tp& __t) { __t.~_Tp(); }
  inline constexpr bool is_trivially_destructible_v<_Tp>
    = __has_trivial_destructor(_Tp);
#else
template <typename _Tp>
  inline constexpr bool is_trivially_destructible_v =
    is_trivially_destructible<_Tp>::value;
#endif

But that won't help C++17.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] c++: mangle function template constraints
  2023-11-22 14:50 ` Jonathan Wakely
  2023-11-22 14:54   ` Jonathan Wakely
@ 2023-12-05 17:05   ` Jonathan Wakely
  2024-01-07 16:40     ` Patrick Palka
  1 sibling, 1 reply; 6+ messages in thread
From: Jonathan Wakely @ 2023-12-05 17:05 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, libstdc++

On Wed, 22 Nov 2023 at 14:50, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Mon, 20 Nov 2023 at 02:56, Jason Merrill wrote:
> >
> > Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
> > push this?
>
> The library parts are OK.
>
> The variable template is_trivially_copyable_v just uses
> __is_trivially_copyable so should be just as efficient, and the change
> to <bit> is fine.
>
> The variable template is_trivially_destructible_v instantiates the
> is_trivially_destructible type trait, which instantiates
> __is_destructible_safe and __is_destructible_impl, which is probably
> why we used the built-in directly in <variant>. But that's an
> acceptable overhead to avoid using the built-in in a mangled context,
> and it would be good to optimize the variable template anyway, as a
> separate change.

This actually causes a regression:

FAIL: 20_util/variant/87619.cc  -std=gnu++20 (test for excess errors)
FAIL: 20_util/variant/87619.cc  -std=gnu++23 (test for excess errors)
FAIL: 20_util/variant/87619.cc  -std=gnu++26 (test for excess errors)

It's OK for C++17 because the changed code is only used for C++20 and later.

That test instantiates a very large variant to check that we don't hit
our template instantiation depth limit. Using the variable template
(which uses the class template) instead of the built-in causes it to
fail now.

So optimizing the variable template is now a priority.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] c++: mangle function template constraints
  2023-12-05 17:05   ` Jonathan Wakely
@ 2024-01-07 16:40     ` Patrick Palka
  2024-01-07 16:45       ` Jonathan Wakely
  0 siblings, 1 reply; 6+ messages in thread
From: Patrick Palka @ 2024-01-07 16:40 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Jason Merrill, gcc-patches, libstdc++

On Tue, 5 Dec 2023, Jonathan Wakely wrote:

> On Wed, 22 Nov 2023 at 14:50, Jonathan Wakely <jwakely@redhat.com> wrote:
> >
> > On Mon, 20 Nov 2023 at 02:56, Jason Merrill wrote:
> > >
> > > Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
> > > push this?
> >
> > The library parts are OK.
> >
> > The variable template is_trivially_copyable_v just uses
> > __is_trivially_copyable so should be just as efficient, and the change
> > to <bit> is fine.
> >
> > The variable template is_trivially_destructible_v instantiates the
> > is_trivially_destructible type trait, which instantiates
> > __is_destructible_safe and __is_destructible_impl, which is probably
> > why we used the built-in directly in <variant>. But that's an
> > acceptable overhead to avoid using the built-in in a mangled context,
> > and it would be good to optimize the variable template anyway, as a
> > separate change.
> 
> This actually causes a regression:
> 
> FAIL: 20_util/variant/87619.cc  -std=gnu++20 (test for excess errors)
> FAIL: 20_util/variant/87619.cc  -std=gnu++23 (test for excess errors)
> FAIL: 20_util/variant/87619.cc  -std=gnu++26 (test for excess errors)
> 
> It's OK for C++17 because the changed code is only used for C++20 and later.
> 
> That test instantiates a very large variant to check that we don't hit
> our template instantiation depth limit. Using the variable template
> (which uses the class template) instead of the built-in causes it to
> fail now.

Could we pass down __trivially_destructible from _Variadic_storage to
_Variadic_union and use that as the dtor's constraint instead of
recursively re-computing it?  This reduces the maximum template
instantiation depth for 87619.cc to ~270 from ~780 so that the depth is
roughly #variants rather than 4 * #variants.

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 20a76c8aa87..4b9002e0917 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -392,7 +392,7 @@ namespace __variant
     };
 
   // Defines members and ctors.
-  template<typename... _Types>
+  template<bool __trivially_destructible, typename... _Types>
     union _Variadic_union
     {
       _Variadic_union() = default;
@@ -401,8 +401,8 @@ namespace __variant
 	_Variadic_union(in_place_index_t<_Np>, _Args&&...) = delete;
     };
 
-  template<typename _First, typename... _Rest>
-    union _Variadic_union<_First, _Rest...>
+  template<bool __trivially_destructible, typename _First, typename... _Rest>
+    union _Variadic_union<__trivially_destructible, _First, _Rest...>
     {
       constexpr _Variadic_union() : _M_rest() { }
 
@@ -427,13 +427,12 @@ namespace __variant
       ~_Variadic_union() = default;
 
       constexpr ~_Variadic_union()
-	requires (!is_trivially_destructible_v<_First>)
-	      || (!is_trivially_destructible_v<_Variadic_union<_Rest...>>)
+	requires (!__trivially_destructible)
       { }
 #endif
 
       _Uninitialized<_First> _M_first;
-      _Variadic_union<_Rest...> _M_rest;
+      _Variadic_union<__trivially_destructible, _Rest...> _M_rest;
     };
 
   // _Never_valueless_alt is true for variant alternatives that can
@@ -514,7 +513,7 @@ namespace __variant
 	return this->_M_index != __index_type(variant_npos);
       }
 
-      _Variadic_union<_Types...> _M_u;
+      _Variadic_union<false, _Types...> _M_u;
       using __index_type = __select_index<_Types...>;
       __index_type _M_index;
     };
@@ -552,7 +551,7 @@ namespace __variant
 	return this->_M_index != static_cast<__index_type>(variant_npos);
       }
 
-      _Variadic_union<_Types...> _M_u;
+      _Variadic_union<true, _Types...> _M_u;
       using __index_type = __select_index<_Types...>;
       __index_type _M_index;
     };

> 
> So optimizing the variable template is now a priority.
> 
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] c++: mangle function template constraints
  2024-01-07 16:40     ` Patrick Palka
@ 2024-01-07 16:45       ` Jonathan Wakely
  0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Wakely @ 2024-01-07 16:45 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Jason Merrill, gcc-patches, libstdc++

On Sun, 7 Jan 2024 at 16:40, Patrick Palka <ppalka@redhat.com> wrote:
>
> On Tue, 5 Dec 2023, Jonathan Wakely wrote:
>
> > On Wed, 22 Nov 2023 at 14:50, Jonathan Wakely <jwakely@redhat.com> wrote:
> > >
> > > On Mon, 20 Nov 2023 at 02:56, Jason Merrill wrote:
> > > >
> > > > Tested x86_64-pc-linux-gnu.  Are the library bits OK?  Any comments before I
> > > > push this?
> > >
> > > The library parts are OK.
> > >
> > > The variable template is_trivially_copyable_v just uses
> > > __is_trivially_copyable so should be just as efficient, and the change
> > > to <bit> is fine.
> > >
> > > The variable template is_trivially_destructible_v instantiates the
> > > is_trivially_destructible type trait, which instantiates
> > > __is_destructible_safe and __is_destructible_impl, which is probably
> > > why we used the built-in directly in <variant>. But that's an
> > > acceptable overhead to avoid using the built-in in a mangled context,
> > > and it would be good to optimize the variable template anyway, as a
> > > separate change.
> >
> > This actually causes a regression:
> >
> > FAIL: 20_util/variant/87619.cc  -std=gnu++20 (test for excess errors)
> > FAIL: 20_util/variant/87619.cc  -std=gnu++23 (test for excess errors)
> > FAIL: 20_util/variant/87619.cc  -std=gnu++26 (test for excess errors)
> >
> > It's OK for C++17 because the changed code is only used for C++20 and later.
> >
> > That test instantiates a very large variant to check that we don't hit
> > our template instantiation depth limit. Using the variable template
> > (which uses the class template) instead of the built-in causes it to
> > fail now.
>
> Could we pass down __trivially_destructible from _Variadic_storage to
> _Variadic_union and use that as the dtor's constraint instead of
> recursively re-computing it?  This reduces the maximum template
> instantiation depth for 87619.cc to ~270 from ~780 so that the depth is
> roughly #variants rather than 4 * #variants.

LGTM.

I think __trivially_destructible should be safe from collisions with
built-ins, as I would expect any such built-in to
be__is_trivially_destructible not __trivially_destructible (we already
have __has_trivial_destructor which we use for that, but that requires
some additional code to use it for the std::is_trivially_destructible
trait, so a built-in to do it directly isn't far-fetched).


>
> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
> index 20a76c8aa87..4b9002e0917 100644
> --- a/libstdc++-v3/include/std/variant
> +++ b/libstdc++-v3/include/std/variant
> @@ -392,7 +392,7 @@ namespace __variant
>      };
>
>    // Defines members and ctors.
> -  template<typename... _Types>
> +  template<bool __trivially_destructible, typename... _Types>
>      union _Variadic_union
>      {
>        _Variadic_union() = default;
> @@ -401,8 +401,8 @@ namespace __variant
>         _Variadic_union(in_place_index_t<_Np>, _Args&&...) = delete;
>      };
>
> -  template<typename _First, typename... _Rest>
> -    union _Variadic_union<_First, _Rest...>
> +  template<bool __trivially_destructible, typename _First, typename... _Rest>
> +    union _Variadic_union<__trivially_destructible, _First, _Rest...>
>      {
>        constexpr _Variadic_union() : _M_rest() { }
>
> @@ -427,13 +427,12 @@ namespace __variant
>        ~_Variadic_union() = default;
>
>        constexpr ~_Variadic_union()
> -       requires (!is_trivially_destructible_v<_First>)
> -             || (!is_trivially_destructible_v<_Variadic_union<_Rest...>>)
> +       requires (!__trivially_destructible)
>        { }
>  #endif
>
>        _Uninitialized<_First> _M_first;
> -      _Variadic_union<_Rest...> _M_rest;
> +      _Variadic_union<__trivially_destructible, _Rest...> _M_rest;
>      };
>
>    // _Never_valueless_alt is true for variant alternatives that can
> @@ -514,7 +513,7 @@ namespace __variant
>         return this->_M_index != __index_type(variant_npos);
>        }
>
> -      _Variadic_union<_Types...> _M_u;
> +      _Variadic_union<false, _Types...> _M_u;
>        using __index_type = __select_index<_Types...>;
>        __index_type _M_index;
>      };
> @@ -552,7 +551,7 @@ namespace __variant
>         return this->_M_index != static_cast<__index_type>(variant_npos);
>        }
>
> -      _Variadic_union<_Types...> _M_u;
> +      _Variadic_union<true, _Types...> _M_u;
>        using __index_type = __select_index<_Types...>;
>        __index_type _M_index;
>      };
>
> >
> > So optimizing the variable template is now a priority.
> >
> >
>


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-01-07 16:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-20  2:55 [PATCH RFC] c++: mangle function template constraints Jason Merrill
2023-11-22 14:50 ` Jonathan Wakely
2023-11-22 14:54   ` Jonathan Wakely
2023-12-05 17:05   ` Jonathan Wakely
2024-01-07 16:40     ` Patrick Palka
2024-01-07 16:45       ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).