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
 
