]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/cp/constraint.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / cp / constraint.cc
index 3e599fe8c47576f2921dc8341f1aee41a9964c07..fef68cf7ab2ad5f8ff1cf7ef29be65f65b43e73d 100644 (file)
@@ -1,5 +1,5 @@
 /* Processing rules for constraints.
-   Copyright (C) 2013-2021 Free Software Foundation, Inc.
+   Copyright (C) 2013-2024 Free Software Foundation, Inc.
    Contributed by Andrew Sutton (andrew.n.sutton@gmail.com)
 
 This file is part of GCC.
@@ -100,18 +100,31 @@ struct subst_info
 
 /* Provides additional context for satisfaction.
 
-   The flag noisy() controls whether to diagnose ill-formed satisfaction,
-   such as the satisfaction value of an atom being non-bool or non-constant.
-
-   The flag diagnose_unsatisfaction_p() controls whether to explain why
-   a constraint is not satisfied.
-
-   The entrypoints to satisfaction for which we set noisy+unsat are
-   diagnose_constraints and diagnose_nested_requirement.  The entrypoints for
-   which we set noisy-unsat are the replays inside constraint_satisfaction_value,
-   evaluate_concept_check and tsubst_nested_requirement.  In other entrypoints,
-   e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
-   cleared).  */
+   During satisfaction:
+    - The flag noisy() controls whether to diagnose ill-formed satisfaction,
+      such as the satisfaction value of an atom being non-bool or non-constant.
+    - The flag diagnose_unsatisfaction_p() controls whether to additionally
+      explain why a constraint is not satisfied.
+    - We enter satisfaction with noisy+unsat from diagnose_constraints.
+    - We enter satisfaction with noisy-unsat from the replay inside
+      constraint_satisfaction_value.
+    - We enter satisfaction quietly (both flags cleared) from
+      constraints_satisfied_p.
+
+   During evaluation of a requires-expression:
+    - The flag noisy() controls whether to diagnose ill-formed types and
+      expressions inside its requirements.
+    - The flag diagnose_unsatisfaction_p() controls whether to additionally
+      explain why the requires-expression evaluates to false.
+    - We enter tsubst_requires_expr with noisy+unsat from
+      diagnose_atomic_constraint and potentially from
+      satisfy_nondeclaration_constraints.
+    - We enter tsubst_requires_expr with noisy-unsat from
+      cp_parser_requires_expression when processing a requires-expression that
+      appears outside a template.
+    - We enter tsubst_requires_expr quietly (both flags cleared) when
+      substituting through a requires-expression as part of template
+      instantiation.  */
 
 struct sat_info : subst_info
 {
@@ -133,7 +146,7 @@ struct sat_info : subst_info
   bool diagnose_unsatisfaction;
 };
 
-static tree satisfy_constraint (tree, tree, sat_info);
+static tree constraint_satisfaction_value (tree, tree, sat_info);
 
 /* True if T is known to be some type other than bool. Note that this
    is false for dependent types and errors.  */
@@ -189,15 +202,8 @@ finish_constraint_binary_op (location_t loc,
     return error_mark_node;
   if (!check_constraint_operands (loc, lhs, rhs))
     return error_mark_node;
-  tree overload;
-  cp_expr expr = build_x_binary_op (loc, code,
-                                   lhs, TREE_CODE (lhs),
-                                   rhs, TREE_CODE (rhs),
-                                   &overload, tf_none);
-  /* When either operand is dependent, the overload set may be non-empty.  */
-  if (expr == error_mark_node)
-    return error_mark_node;
-  expr.set_location (loc);
+  cp_expr expr
+    = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ());
   expr.set_range (lhs.get_start (), rhs.get_finish ());
   return expr;
 }
@@ -234,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
@@ -265,21 +273,6 @@ get_concept_check_template (tree t)
   return tmpl;
 }
 
-/* Returns true if any of the arguments in the template argument list is
-   a wildcard or wildcard pack.  */
-
-bool
-contains_wildcard_p (tree args)
-{
-  for (int i = 0; i < TREE_VEC_LENGTH (args); ++i)
-    {
-      tree arg = TREE_VEC_ELT (args, i);
-      if (TREE_CODE (arg) == WILDCARD_DECL)
-       return true;
-    }
-  return false;
-}
-
 /*---------------------------------------------------------------------------
                     Resolution of qualified concept names
 ---------------------------------------------------------------------------*/
@@ -332,7 +325,7 @@ resolve_function_concept_overload (tree ovl, tree args)
       /* Remember the candidate if we can deduce a substitution.  */
       ++processing_template_decl;
       tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
-      if (tree subst = coerce_template_parms (parms, args, tmpl))
+      if (tree subst = coerce_template_parms (parms, args, tmpl, tf_none))
         {
           if (subst == error_mark_node)
             ++nerrs;
@@ -413,7 +406,7 @@ resolve_concept_check (tree check)
   tree args = TREE_OPERAND (id, 1);
   tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
   ++processing_template_decl;
-  tree result = coerce_template_parms (parms, args, tmpl);
+  tree result = coerce_template_parms (parms, args, tmpl, tf_none);
   --processing_template_decl;
   if (result == error_mark_node)
     return error_mark_node;
@@ -459,8 +452,6 @@ deduce_concept_introduction (tree check)
 /* Build a constrained placeholder type where SPEC is a type-constraint.
    SPEC can be anything were concept_definition_p is true.
 
-   If DECLTYPE_P is true, then the placeholder is decltype(auto).
-
    Returns a pair whose FIRST is the concept being checked and whose
    SECOND is the prototype parameter.  */
 
@@ -594,26 +585,12 @@ map_arguments (tree parms, tree args)
   return parms;
 }
 
-/* Build the parameter mapping for EXPR using ARGS.  */
+/* Build the parameter mapping for EXPR using ARGS, where CTX_PARMS
+   are the template parameters in scope for EXPR.  */
 
 static tree
-build_parameter_mapping (tree expr, tree args, tree decl)
+build_parameter_mapping (tree expr, tree args, tree ctx_parms)
 {
-  tree ctx_parms = NULL_TREE;
-  if (decl)
-    {
-      gcc_assert (TREE_CODE (decl) == TEMPLATE_DECL);
-      ctx_parms = DECL_TEMPLATE_PARMS (decl);
-    }
-  else if (current_template_parms)
-    {
-      /* TODO: This should probably be the only case, but because the
-        point of declaration of concepts is currently set after the
-        initializer, the template parameter lists are not available
-        when normalizing concept definitions, hence the case above.  */
-      ctx_parms = current_template_parms;
-    }
-
   tree parms = find_template_parameters (expr, ctx_parms);
   tree map = map_arguments (parms, args);
   return map;
@@ -645,53 +622,63 @@ parameter_mapping_equivalent_p (tree t1, tree t2)
 
 struct norm_info : subst_info
 {
-  explicit norm_info (tsubst_flags_t complain)
-    : subst_info (tf_warning_or_error | complain, NULL_TREE),
-      context()
+  explicit norm_info (tsubst_flags_t cmp)
+    : norm_info (NULL_TREE, cmp)
   {}
 
   /* Construct a top-level context for DECL.  */
 
   norm_info (tree in_decl, tsubst_flags_t complain)
-    : subst_info (tf_warning_or_error | complain, in_decl),
-      context (make_context (in_decl)),
-      orig_decl (in_decl)
-  {}
-
-  bool generate_diagnostics() const
+    : subst_info (tf_warning_or_error | complain, in_decl)
   {
-    return complain & tf_norm;
+    if (in_decl)
+      {
+       initial_parms = DECL_TEMPLATE_PARMS (in_decl);
+       if (generate_diagnostics ())
+         context = build_tree_list (NULL_TREE, in_decl);
+      }
+    else
+      initial_parms = current_template_parms;
   }
 
-  tree make_context(tree in_decl)
+  bool generate_diagnostics() const
   {
-    if (generate_diagnostics ())
-      return build_tree_list (NULL_TREE, in_decl);
-    return NULL_TREE;
+    return complain & tf_norm;
   }
 
   void update_context(tree expr, tree args)
   {
     if (generate_diagnostics ())
       {
-       tree map = build_parameter_mapping (expr, args, in_decl);
+       tree map = build_parameter_mapping (expr, args, ctx_parms ());
        context = tree_cons (map, expr, context);
       }
     in_decl = get_concept_check_template (expr);
   }
 
+  /* Returns the template parameters that are in scope for the current
+     normalization context.  */
+
+  tree ctx_parms()
+  {
+    if (in_decl)
+      return DECL_TEMPLATE_PARMS (in_decl);
+    else
+      return initial_parms;
+  }
+
   /* Provides information about the source of a constraint. This is a
      TREE_LIST whose VALUE is either a concept check or a constrained
      declaration. The PURPOSE, for concept checks is a parameter mapping
      for that check.  */
 
-  tree context;
+  tree context = NULL_TREE;
 
   /* The declaration whose constraints we're normalizing.  The targets
      of the parameter mapping of each atom will be in terms of the
      template parameters of ORIG_DECL.  */
 
-  tree orig_decl = NULL_TREE;
+  tree initial_parms = NULL_TREE;
 };
 
 static tree normalize_expression (tree, tree, norm_info);
@@ -713,6 +700,45 @@ normalize_logical_operation (tree t, tree args, tree_code c, norm_info info)
   return build2 (c, ci, t0, t1);
 }
 
+/* Data types and hash functions for caching the normal form of a concept-id.
+   This essentially memoizes calls to normalize_concept_check.  */
+
+struct GTY((for_user)) norm_entry
+{
+  /* The CONCEPT_DECL of the concept-id.  */
+  tree tmpl;
+  /* The arguments of the concept-id.  */
+  tree args;
+  /* The normal form of the concept-id.  */
+  tree norm;
+};
+
+struct norm_hasher : ggc_ptr_hash<norm_entry>
+{
+  static hashval_t hash (norm_entry *e)
+  {
+    ++comparing_specializations;
+    hashval_t val = iterative_hash_template_arg (e->tmpl, 0);
+    val = iterative_hash_template_arg (e->args, val);
+    --comparing_specializations;
+    return val;
+  }
+
+  static bool equal (norm_entry *e1, norm_entry *e2)
+  {
+    ++comparing_specializations;
+    bool eq = e1->tmpl == e2->tmpl
+      && template_args_equal (e1->args, e2->args);
+    --comparing_specializations;
+    return eq;
+  }
+};
+
+static GTY((deletable)) hash_table<norm_hasher> *norm_cache;
+
+/* Normalize the concept check CHECK where ARGS are the
+   arguments to be substituted into CHECK's arguments.  */
+
 static tree
 normalize_concept_check (tree check, tree args, norm_info info)
 {
@@ -735,24 +761,41 @@ normalize_concept_check (tree check, tree args, norm_info info)
     targs = tsubst_template_args (targs, args, info.complain, info.in_decl);
   if (targs == error_mark_node)
     return error_mark_node;
+  if (template_args_equal (targs, generic_targs_for (tmpl)))
+    /* Canonicalize generic arguments as NULL_TREE, as an optimization.  */
+    targs = NULL_TREE;
 
   /* Build the substitution for the concept definition.  */
   tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
-  /* Turn on template processing; coercing non-type template arguments
-     will automatically assume they're non-dependent.  */
-  ++processing_template_decl;
-  tree subst = coerce_template_parms (parms, targs, tmpl);
-  --processing_template_decl;
-  if (subst == error_mark_node)
+  if (targs && args)
+    /* As an optimization, coerce the arguments only if necessary
+       (i.e. if they were substituted).  */
+    targs = coerce_template_parms (parms, targs, tmpl, tf_none);
+  if (targs == error_mark_node)
     return error_mark_node;
 
-  /* The concept may have been ill-formed.  */
-  tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
-  if (def == error_mark_node)
-    return error_mark_node;
+  if (!norm_cache)
+    norm_cache = hash_table<norm_hasher>::create_ggc (31);
+  norm_entry *entry = nullptr;
+  if (!info.generate_diagnostics ())
+    {
+      /* Cache the normal form of the substituted concept-id (when not
+        diagnosing).  */
+      norm_entry elt = {tmpl, targs, NULL_TREE};
+      norm_entry **slot = norm_cache->find_slot (&elt, INSERT);
+      if (*slot)
+       return (*slot)->norm;
+      entry = ggc_alloc<norm_entry> ();
+      *entry = elt;
+      *slot = entry;
+    }
 
+  tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
   info.update_context (check, args);
-  return normalize_expression (def, subst, info);
+  tree norm = normalize_expression (def, targs, info);
+  if (entry)
+    entry->norm = norm;
+  return norm;
 }
 
 /* Used by normalize_atom to cache ATOMIC_CONSTRs.  */
@@ -773,12 +816,21 @@ normalize_atom (tree t, tree args, norm_info info)
     return normalize_concept_check (t, args, info);
 
   /* Build the parameter mapping for the atom.  */
-  tree map = build_parameter_mapping (t, args, info.in_decl);
+  tree map = build_parameter_mapping (t, args, info.ctx_parms ());
 
   /* Build a new info object for the atom.  */
   tree ci = build_tree_list (t, info.context);
 
   tree atom = build1 (ATOMIC_CONSTR, ci, map);
+
+  /* Remember whether the expression of this atomic constraint belongs to
+     a concept definition by inspecting in_decl, which should always be set
+     in this case either by norm_info::update_context (when recursing into a
+     concept-id during normalization) or by normalize_concept_definition
+     (when starting out with a concept-id).  */
+  if (info.in_decl && concept_definition_p (info.in_decl))
+    ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (atom) = true;
+
   if (!info.generate_diagnostics ())
     {
       /* Cache the ATOMIC_CONSTRs that we return, so that sat_hasher::equal
@@ -803,10 +855,8 @@ normalize_atom (tree t, tree args, norm_info info)
              tree target = TREE_PURPOSE (node);
              TREE_VEC_ELT (targets, i++) = target;
            }
-         tree ctx_parms = (info.orig_decl
-                           ? DECL_TEMPLATE_PARMS (info.orig_decl)
-                           : current_template_parms);
-         tree target_parms = find_template_parameters (targets, ctx_parms);
+         tree target_parms = find_template_parameters (targets,
+                                                       info.initial_parms);
          TREE_TYPE (map) = target_parms;
        }
 
@@ -879,6 +929,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
      it has the correct template information attached. */
   d = strip_inheriting_ctors (d);
 
+  if (regenerated_lambda_fn_p (d))
+    {
+      /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain
+        all in-scope template parameters, but the lambda from which it was
+        ultimately regenerated does, so use that instead.  */
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d));
+      lambda = most_general_lambda (lambda);
+      d = lambda_function (lambda);
+    }
+
   if (TREE_CODE (d) == TEMPLATE_DECL)
     {
       tmpl = d;
@@ -916,25 +976,22 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
       tmpl = most_general_template (tmpl);
   }
 
+  d = tmpl ? tmpl : decl;
+
   /* If we're not diagnosing errors, use cached constraints, if any.  */
   if (!diag)
-    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
+    if (tree *p = hash_map_safe_get (normalized_map, d))
       return *p;
 
   tree norm = NULL_TREE;
-  if (tree ci = get_constraints (decl))
+  if (tree ci = get_constraints (d))
     {
-      push_nested_class_guard pncs (DECL_CONTEXT (d));
-
-      temp_override<tree> ovr (current_function_decl);
-      if (TREE_CODE (decl) == FUNCTION_DECL)
-       current_function_decl = decl;
-
+      push_access_scope_guard pas (decl);
       norm = get_normalized_constraints_from_info (ci, tmpl, diag);
     }
 
   if (!diag)
-    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+    hash_map_safe_put<hm_ggc> (normalized_map, d, norm);
 
   return norm;
 }
@@ -942,15 +999,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
 /* Returns the normal form of TMPL's definition.  */
 
 static tree
-normalize_concept_definition (tree tmpl, bool diag = false)
+normalize_concept_definition (tree tmpl, bool diag)
 {
+  if (!norm_cache)
+    norm_cache = hash_table<norm_hasher>::create_ggc (31);
+  norm_entry entry = {tmpl, NULL_TREE, NULL_TREE};
+
   if (!diag)
-    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
-      return *p;
+    if (norm_entry *found = norm_cache->find (&entry))
+      return found->norm;
 
-  gcc_assert (concept_definition_p (tmpl));
-  if (OVL_P (tmpl))
-    tmpl = OVL_FIRST (tmpl);
   gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
   tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
   ++processing_template_decl;
@@ -959,38 +1017,35 @@ normalize_concept_definition (tree tmpl, bool diag = false)
   --processing_template_decl;
 
   if (!diag)
-    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+    {
+      norm_entry **slot = norm_cache->find_slot (&entry, INSERT);
+      entry.norm = norm;
+      *slot = ggc_alloc<norm_entry> ();
+      **slot = entry;
+    }
 
   return norm;
 }
 
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_template_requirements (tree tmpl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (tmpl, diag);
-}
-
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_nontemplate_requirements (tree decl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (decl, diag);
-}
-
 /* Normalize an EXPR as a constraint.  */
 
 static tree
-normalize_constraint_expression (tree expr, bool diag)
+normalize_constraint_expression (tree expr, norm_info info)
 {
   if (!expr || expr == error_mark_node)
     return expr;
+
+  if (!info.generate_diagnostics ())
+    if (tree *p = hash_map_safe_get (normalized_map, expr))
+      return *p;
+
   ++processing_template_decl;
-  norm_info info (diag ? tf_norm : tf_none);
   tree norm = get_normalized_constraints (expr, info);
   --processing_template_decl;
+
+  if (!info.generate_diagnostics ())
+    hash_map_safe_put<hm_ggc> (normalized_map, expr, norm);
+
   return norm;
 }
 
@@ -1277,42 +1332,26 @@ remove_constraints (tree t)
    for declaration matching.  */
 
 tree
-maybe_substitute_reqs_for (tree reqs, const_tree decl_)
+maybe_substitute_reqs_for (tree reqs, const_tree decl)
 {
   if (reqs == NULL_TREE)
     return NULL_TREE;
 
-  tree decl = CONST_CAST_TREE (decl_);
-  tree result = STRIP_TEMPLATE (decl);
-
-  if (DECL_UNIQUE_FRIEND_P (result))
+  decl = STRIP_TEMPLATE (decl);
+  if (DECL_UNIQUE_FRIEND_P (decl) && DECL_TEMPLATE_INFO (decl))
     {
-      tree tmpl = decl;
-      if (TREE_CODE (decl) != TEMPLATE_DECL)
-       tmpl = DECL_TI_TEMPLATE (result);
-
-      tree gargs = generic_targs_for (tmpl);
+      tree tmpl = DECL_TI_TEMPLATE (decl);
+      tree outer_args = outer_template_args (decl);
       processing_template_decl_sentinel s;
-      if (uses_template_parms (gargs))
+      if (PRIMARY_TEMPLATE_P (tmpl)
+         || uses_template_parms (outer_args))
        ++processing_template_decl;
-      reqs = tsubst_constraint (reqs, gargs,
+      reqs = tsubst_constraint (reqs, outer_args,
                                tf_warning_or_error, NULL_TREE);
     }
   return reqs;
 }
 
-/* Returns the template-head requires clause for the template
-   declaration T or NULL_TREE if none.  */
-
-tree
-get_template_head_requirements (tree t)
-{
-  tree ci = get_constraints (t);
-  if (!ci)
-    return NULL_TREE;
-  return CI_TEMPLATE_REQS (ci);
-}
-
 /* Returns the trailing requires clause of the declarator of
    a template declaration T or NULL_TREE if none.  */
 
@@ -1422,6 +1461,8 @@ build_standard_check (tree tmpl, tree args, tsubst_flags_t complain)
 {
   gcc_assert (standard_concept_p (tmpl));
   gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
+  if (TREE_DEPRECATED (DECL_TEMPLATE_RESULT (tmpl)))
+    warn_deprecated_use (DECL_TEMPLATE_RESULT (tmpl), NULL_TREE);
   tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
   args = coerce_template_parms (parms, args, tmpl, complain);
   if (args == error_mark_node)
@@ -1566,9 +1607,12 @@ 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 (check, TRUTH_ANDIF_EXPR);
+    check = finish_left_unary_fold_expr (UNKNOWN_LOCATION,
+                                        check, TRUTH_ANDIF_EXPR);
 
   return check;
 }
@@ -1941,22 +1985,44 @@ hash_placeholder_constraint (tree c)
   return val;
 }
 
-/* Substitute through the simple requirement.  */
+/* Substitute through the expression of a simple requirement or
+   compound requirement.  */
 
 static tree
-tsubst_valid_expression_requirement (tree t, tree args, subst_info info)
+tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
 {
-  tree r = tsubst_expr (t, args, info.complain, info.in_decl, false);
-  if (convert_to_void (r, ICV_STATEMENT, info.complain) == error_mark_node)
-    return error_mark_node;
-  return r;
+  tree r = tsubst_expr (t, args, tf_none, info.in_decl);
+  if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
+    return r;
+
+  if (info.diagnose_unsatisfaction_p ())
+    {
+      location_t loc = cp_expr_loc_or_input_loc (t);
+      if (diagnosing_failed_constraint::replay_errors_p ())
+       {
+         inform (loc, "the required expression %qE is invalid, because", t);
+         if (r == error_mark_node)
+           tsubst_expr (t, args, info.complain, info.in_decl);
+         else
+           convert_to_void (r, ICV_STATEMENT, info.complain);
+       }
+      else
+       inform (loc, "the required expression %qE is invalid", t);
+    }
+  else if (info.noisy ())
+    {
+      r = tsubst_expr (t, args, info.complain, info.in_decl);
+      convert_to_void (r, ICV_STATEMENT, info.complain);
+    }
+
+  return error_mark_node;
 }
 
 
 /* Substitute through the simple requirement.  */
 
 static tree
-tsubst_simple_requirement (tree t, tree args, subst_info info)
+tsubst_simple_requirement (tree t, tree args, sat_info info)
 {
   tree t0 = TREE_OPERAND (t, 0);
   tree expr = tsubst_valid_expression_requirement (t0, args, info);
@@ -1965,13 +2031,41 @@ tsubst_simple_requirement (tree t, tree args, subst_info info)
   return boolean_true_node;
 }
 
+/* Subroutine of tsubst_type_requirement that performs the actual substitution
+   and diagnosing.  Also used by tsubst_compound_requirement.  */
+
+static tree
+tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc)
+{
+  tree r = tsubst (t, args, tf_none, info.in_decl);
+  if (r != error_mark_node)
+    return r;
+
+  if (info.diagnose_unsatisfaction_p ())
+    {
+      if (diagnosing_failed_constraint::replay_errors_p ())
+       {
+         /* Replay the substitution error.  */
+         inform (loc, "the required type %qT is invalid, because", t);
+         tsubst (t, args, info.complain, info.in_decl);
+       }
+      else
+       inform (loc, "the required type %qT is invalid", t);
+    }
+  else if (info.noisy ())
+    tsubst (t, args, info.complain, info.in_decl);
+
+  return error_mark_node;
+}
+
+
 /* Substitute through the type requirement.  */
 
 static tree
-tsubst_type_requirement (tree t, tree args, subst_info info)
+tsubst_type_requirement (tree t, tree args, sat_info info)
 {
   tree t0 = TREE_OPERAND (t, 0);
-  tree type = tsubst (t0, args, info.complain, info.in_decl);
+  tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
   if (type == error_mark_node)
     return error_mark_node;
   return boolean_true_node;
@@ -1987,39 +2081,11 @@ type_deducible_p (tree expr, tree type, tree placeholder, tree args,
      references are preserved in the result.  */
   expr = force_paren_expr_uneval (expr);
 
-  /* Replace the constraints with the instantiated constraints. This
-     substitutes args into any template parameters in the trailing
-     result type.  */
-  tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
-  tree subst_constr
-    = tsubst_constraint (saved_constr,
-                        args,
-                        info.complain | tf_partial,
-                        info.in_decl);
-
-  if (subst_constr == error_mark_node)
-    return false;
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr;
-
-  /* Temporarily unlink the canonical type.  */
-  tree saved_type = TYPE_CANONICAL (placeholder);
-  TYPE_CANONICAL (placeholder) = NULL_TREE;
-
-  tree deduced_type
-    = do_auto_deduction (type,
-                        expr,
-                        placeholder,
-                        info.complain,
-                        adc_requirement);
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr;
-  TYPE_CANONICAL (placeholder) = saved_type;
-
-  if (deduced_type == error_mark_node)
-    return false;
+  tree deduced_type = do_auto_deduction (type, expr, placeholder,
+                                        info.complain, adc_requirement,
+                                        /*outer_targs=*/args);
 
-  return true;
+  return deduced_type != error_mark_node;
 }
 
 /* True if EXPR can not be converted to TYPE.  */
@@ -2048,7 +2114,7 @@ expression_convertible_p (tree expr, tree type, subst_info info)
 /* Substitute through the compound requirement.  */
 
 static tree
-tsubst_compound_requirement (tree t, tree args, subst_info info)
+tsubst_compound_requirement (tree t, tree args, sat_info info)
 {
   tree t0 = TREE_OPERAND (t, 0);
   tree t1 = TREE_OPERAND (t, 1);
@@ -2056,13 +2122,20 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
   if (expr == error_mark_node)
     return error_mark_node;
 
+  location_t loc = cp_expr_loc_or_input_loc (expr);
+
   /* Check the noexcept condition.  */
   bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
   if (noexcept_p && !expr_noexcept_p (expr, tf_none))
-    return error_mark_node;
+    {
+      if (info.diagnose_unsatisfaction_p ())
+       inform (loc, "%qE is not %<noexcept%>", expr);
+      else
+       return error_mark_node;
+    }
 
   /* Substitute through the type expression, if any.  */
-  tree type = tsubst (t1, args, info.complain, info.in_decl);
+  tree type = tsubst_type_requirement_1 (t1, args, info, EXPR_LOCATION (t));
   if (type == error_mark_node)
     return error_mark_node;
 
@@ -2074,38 +2147,76 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
       if (tree placeholder = type_uses_auto (type))
        {
          if (!type_deducible_p (expr, type, placeholder, args, quiet))
-           return error_mark_node;
+           {
+             if (info.diagnose_unsatisfaction_p ())
+               {
+                 if (diagnosing_failed_constraint::replay_errors_p ())
+                   {
+                     inform (loc,
+                             "%qE does not satisfy return-type-requirement, "
+                             "because", t0);
+                     /* Further explain the reason for the error.  */
+                     type_deducible_p (expr, type, placeholder, args, info);
+                   }
+                 else
+                   inform (loc,
+                           "%qE does not satisfy return-type-requirement", t0);
+               }
+             return error_mark_node;
+           }
        }
       else if (!expression_convertible_p (expr, type, quiet))
-       return error_mark_node;
+       {
+         if (info.diagnose_unsatisfaction_p ())
+           {
+             if (diagnosing_failed_constraint::replay_errors_p ())
+               {
+                 inform (loc, "cannot convert %qE to %qT because", t0, type);
+                 /* Further explain the reason for the error.  */
+                 expression_convertible_p (expr, type, info);
+               }
+             else
+               inform (loc, "cannot convert %qE to %qT", t0, type);
+           }
+         return error_mark_node;
+       }
     }
 
   return boolean_true_node;
 }
 
+/* Substitute through the nested requirement.  */
+
 static tree
-tsubst_nested_requirement (tree t, tree args, subst_info info)
+tsubst_nested_requirement (tree t, tree args, sat_info info)
 {
-  /* Perform satisfaction quietly with the regular normal form.  */
   sat_info quiet (tf_none, info.in_decl);
-  tree norm = TREE_VALUE (TREE_TYPE (t));
-  tree diag_norm = TREE_PURPOSE (TREE_TYPE (t));
-  tree result = satisfy_constraint (norm, args, quiet);
-  if (result == error_mark_node)
+  tree result = constraint_satisfaction_value (t, args, quiet);
+  if (result == boolean_true_node)
+    return boolean_true_node;
+
+  if (result == boolean_false_node
+      && info.diagnose_unsatisfaction_p ())
     {
-      /* Replay the error using the diagnostic normal form.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      satisfy_constraint (diag_norm, args, noisy);
+      tree expr = TREE_OPERAND (t, 0);
+      location_t loc = cp_expr_location (t);
+      if (diagnosing_failed_constraint::replay_errors_p ())
+       {
+         /* Replay the substitution error.  */
+         inform (loc, "nested requirement %qE is not satisfied, because", expr);
+         constraint_satisfaction_value (t, args, info);
+       }
+      else
+       inform (loc, "nested requirement %qE is not satisfied", expr);
     }
-  if (result != boolean_true_node)
-    return error_mark_node;
-  return boolean_true_node;
+
+  return error_mark_node;
 }
 
 /* Substitute ARGS into the requirement T.  */
 
 static tree
-tsubst_requirement (tree t, tree args, subst_info info)
+tsubst_requirement (tree t, tree args, sat_info info)
 {
   iloc_sentinel loc_s (cp_expr_location (t));
   switch (TREE_CODE (t))
@@ -2195,34 +2306,30 @@ tsubst_constraint_variables (tree t, tree args, subst_info info)
    in its requirements ... In such cases, the expression evaluates
    to false; it does not cause the program to be ill-formed.
 
-   However, there are cases where substitution must produce a
-   new requires-expression, that is not a template constraint.
-   For example:
+   When substituting through a REQUIRES_EXPR as part of template
+   instantiation, we call this routine with info.quiet() true.
 
-        template<typename T>
-        class X {
-          template<typename U>
-          static constexpr bool var = requires (U u) { T::fn(u); };
-        };
+   When evaluating a REQUIRES_EXPR that appears outside a template in
+   cp_parser_requires_expression, we call this routine with
+   info.noisy() true.
 
-   In the instantiation of X<Y> (assuming Y defines fn), then the
-   instantiated requires-expression would include Y::fn(u). If any
-   substitution in the requires-expression fails, we can immediately
-   fold the expression to false, as would be the case e.g., when
-   instantiation X<int>.  */
+   Finally, when diagnosing unsatisfaction from diagnose_atomic_constraint
+   and when diagnosing a false REQUIRES_EXPR via diagnose_constraints,
+   we call this routine with info.diagnose_unsatisfaction_p() true.  */
 
-tree
-tsubst_requires_expr (tree t, tree args,
-                     tsubst_flags_t complain, tree in_decl)
+static tree
+tsubst_requires_expr (tree t, tree args, sat_info info)
 {
   local_specialization_stack stack (lss_copy);
 
-  subst_info info (complain, in_decl);
+  /* We need to check access during the substitution.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
 
   /* A requires-expression is an unevaluated context.  */
   cp_unevaluated u;
 
-  args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args);
+  args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args,
+                        info.complain, info.in_decl);
   if (processing_template_decl)
     {
       /* We're partially instantiating a generic lambda.  Substituting into
@@ -2230,7 +2337,7 @@ tsubst_requires_expr (tree t, tree args,
         checked out of order, so instead just remember the template
         arguments and wait until we can substitute them all at once.  */
       t = copy_node (t);
-      REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain);
+      REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
       return t;
     }
 
@@ -2241,14 +2348,30 @@ tsubst_requires_expr (tree t, tree args,
        return boolean_false_node;
     }
 
+  tree result = boolean_true_node;
   for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
     {
       tree req = TREE_VALUE (reqs);
-      tree result = tsubst_requirement (req, args, info);
-      if (result == error_mark_node)
-       return boolean_false_node;
+      if (tsubst_requirement (req, args, info) == error_mark_node)
+       {
+         result = boolean_false_node;
+         if (info.diagnose_unsatisfaction_p ())
+           /* Keep going so that we diagnose all failed requirements.  */;
+         else
+           break;
+       }
     }
-  return boolean_true_node;
+  return result;
+}
+
+/* Public wrapper for the above.  */
+
+tree
+tsubst_requires_expr (tree t, tree args,
+                     tsubst_flags_t complain, tree in_decl)
+{
+  sat_info info (complain, in_decl);
+  return tsubst_requires_expr (t, args, info);
 }
 
 /* Substitute ARGS into the constraint information CI, producing a new
@@ -2286,49 +2409,21 @@ tsubst_parameter_mapping (tree map, tree args, subst_info info)
         return error_mark_node;
       tree parm = TREE_VALUE (p);
       tree arg = TREE_PURPOSE (p);
-      tree new_arg = NULL_TREE;
-      if (TYPE_P (arg))
-        {
-          /* If a template parameter is declared with a placeholder, we can
-             get those in the argument list if decltype is applied to the
-             placeholder. For example:
-
-               template<auto T>
-                 requires C<decltype(T)>
-               void f() { }
-
-            The normalized argument for C will be an auto type, so we'll
-             need to deduce the actual argument from the corresponding
-             initializer (whatever argument is provided for T), and use
-             that result in the instantiated parameter mapping.  */
-          if (tree auto_node = type_uses_auto (arg))
-            {
-              int level;
-              int index;
-             template_parm_level_and_index (parm, &level, &index);
-             tree init = TMPL_ARG (args, level, index);
-              new_arg = do_auto_deduction (arg, init, auto_node,
-                                          complain, adc_variable_type,
-                                          make_tree_vec (0));
-            }
-        }
-      else if (ARGUMENT_PACK_P (arg))
+      tree new_arg;
+      if (ARGUMENT_PACK_P (arg))
        new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
-      if (!new_arg)
+      else
        {
          new_arg = tsubst_template_arg (arg, args, complain, in_decl);
          if (TYPE_P (new_arg))
            new_arg = canonicalize_type_argument (new_arg, complain);
-         if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
-           {
-             tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
-             for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
-               {
-                 tree& pack_arg = TREE_VEC_ELT (pack_args, i);
-                 if (TYPE_P (pack_arg))
-                   pack_arg = canonicalize_type_argument (pack_arg, complain);
-               }
-           }
+       }
+      if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
+       {
+         tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
+         for (tree& pack_arg : tree_vec_range (pack_args))
+           if (TYPE_P (pack_arg))
+             pack_arg = canonicalize_type_argument (pack_arg, complain);
        }
       if (new_arg == error_mark_node)
        return error_mark_node;
@@ -2436,6 +2531,9 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 {
   static hashval_t hash (sat_entry *e)
   {
+    auto cso = make_temp_override (comparing_specializations);
+    ++comparing_specializations;
+
     if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e->atom))
       {
        /* Atoms with instantiated mappings are built during satisfaction.
@@ -2470,6 +2568,9 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 
   static bool equal (sat_entry *e1, sat_entry *e2)
   {
+    auto cso = make_temp_override (comparing_specializations);
+    ++comparing_specializations;
+
     if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e1->atom)
        != ATOMIC_CONSTR_MAP_INSTANTIATED_P (e2->atom))
       return false;
@@ -2504,7 +2605,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 /* Cache the result of satisfy_atom.  */
 static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
 
-/* Cache the result of constraint_satisfaction_value.  */
+/* Cache the result of satisfy_declaration_constraints.  */
 static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
 
 /* A tool used by satisfy_atom to help manage satisfaction caching and to
@@ -2578,9 +2679,16 @@ satisfaction_cache
       *slot = entry;
     }
   else
-    /* We shouldn't get here, but if we do, let's just leave 'entry'
-       empty, effectively disabling the cache.  */
-    return;
+    {
+      /* We're evaluating this atom for the first time, and doing so noisily.
+        This shouldn't happen outside of error recovery situations involving
+        unstable satisfaction.  Let's just leave 'entry' empty, effectively
+        disabling the cache, and remove the empty slot.  */
+      gcc_checking_assert (seen_error ());
+      /* Appease hash_table::check_complete_insertion.  */
+      *slot = ggc_alloc<sat_entry> ();
+      sat_cache->clear_slot (slot);
+    }
 }
 
 /* Returns the cached satisfaction result if we have one and we're not
@@ -2596,7 +2704,7 @@ satisfaction_cache::get ()
   if (entry->evaluating)
     {
       /* If we get here, it means satisfaction is self-recursive.  */
-      gcc_checking_assert (!entry->result);
+      gcc_checking_assert (!entry->result || seen_error ());
       if (info.noisy ())
        error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)),
                  "satisfaction of atomic constraint %qE depends on itself",
@@ -2680,7 +2788,8 @@ tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   /* We also don't want to evaluate concept-checks when substituting the
      constraint-expressions of a declaration.  */
   processing_constraint_expression_sentinel s;
-  tree expr = tsubst_expr (t, args, complain, in_decl, false);
+  cp_unevaluated u;
+  tree expr = tsubst_expr (t, args, complain, in_decl);
   return expr;
 }
 
@@ -2796,40 +2905,45 @@ satisfaction_value (tree t)
     return t;
 
   gcc_assert (TREE_CODE (t) == INTEGER_CST
-             && same_type_p (TREE_TYPE (t), boolean_type_node));
+             && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (t),
+                                                           boolean_type_node));
   if (integer_zerop (t))
     return boolean_false_node;
   else
     return boolean_true_node;
 }
 
-/* Build a new template argument list with template arguments corresponding
-   to the parameters used in an atomic constraint.  */
+/* Build a new template argument vector corresponding to the parameter
+   mapping of the atomic constraint T, using arguments from ARGS.  */
 
-tree
-get_mapped_args (tree map)
+static tree
+get_mapped_args (tree t, tree args)
 {
+  tree map = ATOMIC_CONSTR_MAP (t);
+
   /* No map, no arguments.  */
   if (!map)
     return NULL_TREE;
 
-  /* Find the mapped parameter with the highest level.  */
-  int count = 0;
-  for (tree p = map; p; p = TREE_CHAIN (p))
-    {
-      int level;
-      int index;
-      template_parm_level_and_index (TREE_VALUE (p), &level, &index);
-      if (level > count)
-        count = level;
-    }
+  /* Determine the depth of the resulting argument vector.  */
+  int depth;
+  if (ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (t))
+    /* The expression of this atomic constraint comes from a concept definition,
+       whose template depth is always one, so the resulting argument vector
+       will also have depth one.  */
+    depth = 1;
+  else
+    /* Otherwise, the expression of this atomic constraint comes from
+       the context of the constrained entity, whose template depth is that
+       of ARGS.  */
+    depth = TMPL_ARGS_DEPTH (args);
 
   /* Place each argument at its corresponding position in the argument
      list. Note that the list will be sparse (not all arguments supplied),
      but instantiation is guaranteed to only use the parameters in the
      mapping, so null arguments would never be used.  */
-  auto_vec< vec<tree> > lists (count);
-  lists.quick_grow_cleared (count);
+  auto_vec< vec<tree> > lists (depth);
+  lists.quick_grow_cleared (depth);
   for (tree p = map; p; p = TREE_CHAIN (p))
     {
       int level;
@@ -2839,12 +2953,12 @@ get_mapped_args (tree map)
       /* Insert the argument into its corresponding position.  */
       vec<tree> &list = lists[level - 1];
       if (index >= (int)list.length ())
-       list.safe_grow_cleared (index + 1, true);
+       list.safe_grow_cleared (index + 1, /*exact=*/false);
       list[index] = TREE_PURPOSE (p);
     }
 
   /* Build the new argument list.  */
-  tree args = make_tree_vec (lists.length ());
+  args = make_tree_vec (lists.length ());
   for (unsigned i = 0; i != lists.length (); ++i)
     {
       vec<tree> &list = lists[i];
@@ -2856,10 +2970,19 @@ get_mapped_args (tree map)
     }
   SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
 
+  if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)
+      && TMPL_ARGS_DEPTH (args) == 1)
+    {
+      /* Get rid of the redundant outer TREE_VEC.  */
+      tree level = TMPL_ARGS_LEVEL (args, 1);
+      ggc_free (args);
+      args = level;
+    }
+
   return args;
 }
 
-static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
+static void diagnose_atomic_constraint (tree, tree, tree, sat_info);
 
 /* Compute the satisfaction of an atomic constraint.  */
 
@@ -2910,17 +3033,17 @@ satisfy_atom (tree t, tree args, sat_info info)
     }
 
   /* Rebuild the argument vector from the parameter mapping.  */
-  args = get_mapped_args (map);
+  args = get_mapped_args (t, args);
 
   /* Apply the parameter mapping (i.e., just substitute).  */
   tree expr = ATOMIC_CONSTR_EXPR (t);
-  tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl, false);
+  tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl);
   if (result == error_mark_node)
     {
       /* If substitution results in an invalid type or expression, the constraint
         is not satisfied. Replay the substitution.  */
       if (info.diagnose_unsatisfaction_p ())
-       tsubst_expr (expr, args, info.complain, info.in_decl, false);
+       tsubst_expr (expr, args, info.complain, info.in_decl);
       return cache.save (inst_cache.save (boolean_false_node));
     }
 
@@ -2932,23 +3055,25 @@ satisfy_atom (tree t, tree args, sat_info info)
   if (!same_type_p (TREE_TYPE (result), boolean_type_node))
     {
       if (info.noisy ())
-       diagnose_atomic_constraint (t, map, result, info);
+       diagnose_atomic_constraint (t, args, result, info);
       return cache.save (inst_cache.save (error_mark_node));
     }
 
   /* Compute the value of the constraint.  */
   if (info.noisy ())
-    result = cxx_constant_value (result);
+    {
+      iloc_sentinel ils (EXPR_LOCATION (result));
+      result = cxx_constant_value (result);
+    }
   else
     {
-      result = maybe_constant_value (result, NULL_TREE,
-                                    /*manifestly_const_eval=*/true);
+      result = maybe_constant_value (result, NULL_TREE, mce_true);
       if (!TREE_CONSTANT (result))
        result = error_mark_node;
     }
   result = satisfaction_value (result);
   if (result == boolean_false_node && info.diagnose_unsatisfaction_p ())
-    diagnose_atomic_constraint (t, map, result, info);
+    diagnose_atomic_constraint (t, args, result, info);
 
   return cache.save (inst_cache.save (result));
 }
@@ -2986,7 +3111,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info)
 /* Check that the normalized constraint T is satisfied for ARGS.  */
 
 static tree
-satisfy_constraint (tree t, tree args, sat_info info)
+satisfy_normalized_constraints (tree t, tree args, sat_info info)
 {
   auto_timevar time (TV_CONSTRAINT_SAT);
 
@@ -2999,68 +3124,101 @@ satisfy_constraint (tree t, tree args, sat_info info)
   /* We need to check access during satisfaction.  */
   deferring_access_check_sentinel acs (dk_no_deferred);
 
+  /* Constraints are unevaluated operands.  */
+  cp_unevaluated u;
+
   return satisfy_constraint_r (t, args, info);
 }
 
-/* Check the normalized constraints T against ARGS, returning a satisfaction
-   value (either true, false, or error).  */
+/* Return the normal form of the constraints on the placeholder 'auto'
+   type T.  */
 
 static tree
-satisfy_associated_constraints (tree t, tree args, sat_info info)
+normalize_placeholder_type_constraints (tree t, bool diag)
 {
-  /* If there are no constraints then this is trivially satisfied.  */
-  if (!t)
-    return boolean_true_node;
+  gcc_assert (is_auto (t));
+  tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t);
+  if (!ci)
+    return NULL_TREE;
 
-  /* If any arguments depend on template parameters, we can't
-     check constraints. Pretend they're satisfied for now.  */
-  if (args && uses_template_parms (args))
-    return boolean_true_node;
+  tree constr = TREE_VALUE (ci);
+  /* The TREE_PURPOSE contains the set of template parameters that were in
+     scope for this placeholder type; use them as the initial template
+     parameters for normalization.  */
+  tree initial_parms = TREE_PURPOSE (ci);
+
+  /* The 'auto' itself is used as the first argument in its own constraints,
+     and its level is one greater than its template depth.  So in order to
+     capture all used template parameters, we need to add an extra level of
+     template parameters to the context; a dummy level suffices.  */
+  initial_parms
+    = tree_cons (size_int (initial_parms
+                          ? TMPL_PARMS_DEPTH (initial_parms) + 1 : 1),
+                make_tree_vec (0), initial_parms);
 
-  return satisfy_constraint (t, args, info);
+  norm_info info (diag ? tf_norm : tf_none);
+  info.initial_parms = initial_parms;
+  return normalize_constraint_expression (constr, info);
 }
 
-/* Evaluate EXPR as a constraint expression using ARGS, returning a
-   satisfaction value. */
+/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
+   Here, T can be a concept-id, nested-requirement, placeholder 'auto', or
+   requires-expression.  */
 
 static tree
-satisfy_constraint_expression (tree t, tree args, sat_info info)
+satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
 
-  gcc_assert (EXPR_P (t));
+  /* Handle REQUIRES_EXPR directly, bypassing satisfaction.  */
+  if (TREE_CODE (t) == REQUIRES_EXPR)
+    {
+      auto ovr = make_temp_override (current_constraint_diagnosis_depth);
+      if (info.noisy ())
+       ++current_constraint_diagnosis_depth;
+      return tsubst_requires_expr (t, args, info);
+    }
 
   /* Get the normalized constraints.  */
   tree norm;
-  if (args == NULL_TREE && concept_check_p (t))
+  if (concept_check_p (t))
     {
+      gcc_assert (!args);
       tree id = unpack_concept_check (t);
       args = TREE_OPERAND (id, 1);
       tree tmpl = get_concept_check_template (id);
       norm = normalize_concept_definition (tmpl, info.noisy ());
     }
+  else if (TREE_CODE (t) == NESTED_REQ)
+    {
+      norm_info ninfo (info.noisy () ? tf_norm : tf_none);
+      /* The TREE_TYPE contains the set of template parameters that were in
+        scope for this nested requirement; use them as the initial template
+        parameters for normalization.  */
+      ninfo.initial_parms = TREE_TYPE (t);
+      norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
+    }
+  else if (is_auto (t))
+    {
+      norm = normalize_placeholder_type_constraints (t, info.noisy ());
+      if (!norm)
+       return boolean_true_node;
+    }
   else
-    norm = normalize_constraint_expression (t, info.noisy ());
+    gcc_unreachable ();
 
   /* Perform satisfaction.  */
-  return satisfy_constraint (norm, args, info);
+  return satisfy_normalized_constraints (norm, args, info);
 }
 
-/* Used only to evaluate requires-expressions during constant expression
-   evaluation.  */
-
-tree
-satisfy_constraint_expression (tree expr)
-{
-  sat_info info (tf_none, NULL_TREE);
-  return satisfy_constraint_expression (expr, NULL_TREE, info);
-}
+/* Evaluate the associated constraints of the template specialization T
+   according to INFO, returning a satisfaction value.  */
 
 static tree
 satisfy_declaration_constraints (tree t, sat_info info)
 {
-  gcc_assert (DECL_P (t));
+  gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
   const tree saved_t = t;
 
   /* For inherited constructors, consider the original declaration;
@@ -3080,25 +3238,42 @@ satisfy_declaration_constraints (tree t, sat_info info)
     if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
       return *result;
 
-  /* Get the normalized constraints.  */
-  tree norm = NULL_TREE;
   tree args = NULL_TREE;
   if (tree ti = DECL_TEMPLATE_INFO (t))
     {
-      tree tmpl = TI_TEMPLATE (ti);
-      norm = normalize_template_requirements (tmpl, info.noisy ());
-
       /* The initial parameter mapping is the complete set of
         template arguments substituted into the declaration.  */
       args = TI_ARGS (ti);
       if (inh_ctor_targs)
        args = add_outermost_template_args (args, inh_ctor_targs);
     }
-  else
+
+  if (regenerated_lambda_fn_p (t))
     {
-      /* These should be empty until we allow constraints on non-templates.  */
-      norm = normalize_nontemplate_requirements (t, info.noisy ());
-    }
+      /* The TI_ARGS of a regenerated lambda contains only the innermost
+        set of template arguments.  Augment this with the outer template
+        arguments that were used to regenerate the lambda.  */
+      gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
+      tree regen_args = lambda_regenerating_args (t);
+      if (args)
+       args = add_to_template_args (regen_args, args);
+      else
+       args = regen_args;
+    }
+
+  /* If the innermost arguments are dependent, or if the outer arguments
+     are dependent and are needed by the constraints, we can't check
+     satisfaction yet so pretend they're satisfied for now.  */
+  if (uses_template_parms (args)
+      && ((DECL_TEMPLATE_INFO (t)
+          && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))
+          && (TMPL_ARGS_DEPTH (args) == 1
+              || uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))))
+         || uses_outer_template_parms_in_constraints (t)))
+    return boolean_true_node;
+
+  /* Get the normalized constraints.  */
+  tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
 
   unsigned ftc_count = vec_safe_length (failed_type_completions);
 
@@ -3107,9 +3282,11 @@ satisfy_declaration_constraints (tree t, sat_info info)
     {
       if (!push_tinst_level (t))
        return result;
+      push_to_top_level ();
       push_access_scope (t);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (t);
+      pop_from_top_level ();
       pop_tinst_level ();
     }
 
@@ -3131,6 +3308,10 @@ satisfy_declaration_constraints (tree t, sat_info info)
   return result;
 }
 
+/* Evaluate the associated constraints of the template T using ARGS as the
+   innermost set of template arguments and according to INFO, returning a
+   satisfaction value.  */
+
 static tree
 satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
@@ -3139,79 +3320,87 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
 
   gcc_assert (TREE_CODE (t) == TEMPLATE_DECL);
 
-  args = add_outermost_template_args (t, args);
+  if (regenerated_lambda_fn_p (t))
+    {
+      /* As in the two-parameter version of this function.  */
+      gcc_assert (TMPL_ARGS_DEPTH (args) == 1);
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
+      tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda));
+      args = add_to_template_args (outer_args, args);
+    }
+  else
+    args = add_outermost_template_args (t, args);
+
+  /* If the innermost arguments are dependent, or if the outer arguments
+     are dependent and are needed by the constraints, we can't check
+     satisfaction yet so pretend they're satisfied for now.  */
+  if (uses_template_parms (args)
+      && (TMPL_ARGS_DEPTH (args) == 1
+         || uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))
+         || uses_outer_template_parms_in_constraints (t)))
+    return boolean_true_node;
 
   tree result = boolean_true_node;
-  if (tree norm = normalize_template_requirements (t, info.noisy ()))
+  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
     {
       if (!push_tinst_level (t, args))
        return result;
       tree pattern = DECL_TEMPLATE_RESULT (t);
+      push_to_top_level ();
       push_access_scope (pattern);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (pattern);
+      pop_from_top_level ();
       pop_tinst_level ();
     }
 
   return result;
 }
 
-static tree
-constraint_satisfaction_value (tree t, sat_info info)
-{
-  tree r;
-  if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, info);
-  else
-    r = satisfy_constraint_expression (t, NULL_TREE, info);
-  if (r == error_mark_node && info.quiet ()
-      && !(DECL_P (t) && TREE_NO_WARNING (t)))
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      constraint_satisfaction_value (t, noisy);
-      if (DECL_P (t))
-       /* Avoid giving these errors again.  */
-       TREE_NO_WARNING (t) = true;
-    }
-  return r;
-}
+/* A wrapper around satisfy_declaration_constraints and
+   satisfy_nondeclaration_constraints which additionally replays
+   quiet ill-formed satisfaction noisily, so that ill-formed
+   satisfaction always gets diagnosed.  */
 
 static tree
 constraint_satisfaction_value (tree t, tree args, sat_info info)
 {
   tree r;
   if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, args, info);
+    {
+      if (args)
+       r = satisfy_declaration_constraints (t, args, info);
+      else
+       r = satisfy_declaration_constraints (t, info);
+    }
   else
-    r = satisfy_constraint_expression (t, args, info);
-  if (r == error_mark_node && info.quiet ())
+    r = satisfy_nondeclaration_constraints (t, args, info);
+  if (r == error_mark_node && info.quiet ()
+      && !(DECL_P (t) && warning_suppressed_p (t)))
     {
-      /* Replay the error with re-normalized requirements.  */
+      /* Replay the error noisily.  */
       sat_info noisy (tf_warning_or_error, info.in_decl);
       constraint_satisfaction_value (t, args, noisy);
+      if (DECL_P (t) && !args)
+       /* Avoid giving these errors again.  */
+       suppress_warning (t);
     }
   return r;
 }
 
-/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
-   otherwise, even in the case of errors.  */
+/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
+   and false otherwise, even in the case of errors.
 
-bool
-constraints_satisfied_p (tree t)
-{
-  if (!flag_concepts)
-    return true;
-
-  sat_info quiet (tf_none, NULL_TREE);
-  return constraint_satisfaction_value (t, quiet) == boolean_true_node;
-}
-
-/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
-    and false otherwise, even in the case of errors.  */
+   Here, T can be:
+     - a template declaration
+     - a template specialization (in which case ARGS must be empty)
+     - a concept-id (in which case ARGS must be empty)
+     - a nested-requirement
+     - a placeholder 'auto'
+     - a requires-expression.  */
 
 bool
-constraints_satisfied_p (tree t, tree args)
+constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
 {
   if (!flag_concepts)
     return true;
@@ -3224,7 +3413,7 @@ constraints_satisfied_p (tree t, tree args)
    evaluation of template-ids as id-expressions.  */
 
 tree
-evaluate_concept_check (tree check, tsubst_flags_t complain)
+evaluate_concept_check (tree check)
 {
   if (check == error_mark_node)
     return error_mark_node;
@@ -3233,14 +3422,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
 
   /* Check for satisfaction without diagnostics.  */
   sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
-  if (result == error_mark_node && (complain & tf_error))
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, NULL_TREE);
-      satisfy_constraint_expression (check, NULL_TREE, noisy);
-    }
-  return result;
+  return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
+}
+
+/* Evaluate the requires-expression T, returning either boolean_true_node
+   or boolean_false_node.  This is used during folding and constexpr
+   evaluation.  */
+
+tree
+evaluate_requires_expr (tree t)
+{
+  gcc_assert (TREE_CODE (t) == REQUIRES_EXPR);
+  sat_info quiet (tf_none, NULL_TREE);
+  return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet);
 }
 
 /*---------------------------------------------------------------------------
@@ -3253,15 +3447,6 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
 tree
 finish_requires_expr (location_t loc, tree parms, tree reqs)
 {
-  /* Modify the declared parameters by removing their context
-     so they don't refer to the enclosing scope and explicitly
-     indicating that they are constraint variables. */
-  for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
-    {
-      DECL_CONTEXT (parm) = NULL_TREE;
-      CONSTRAINT_VAR_P (parm) = true;
-    }
-
   /* Build the node. */
   tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs, NULL_TREE);
   TREE_SIDE_EFFECTS (r) = false;
@@ -3310,15 +3495,9 @@ finish_compound_requirement (location_t loc, tree expr, tree type, bool noexcept
 tree
 finish_nested_requirement (location_t loc, tree expr)
 {
-  /* We need to normalize the constraints now, at parse time, while
-     we have the necessary template context.  We normalize twice,
-     once without diagnostic information and once with, which we'll
-     later use for quiet and noisy satisfaction respectively.  */
-  tree norm = normalize_constraint_expression (expr, /*diag=*/false);
-  tree diag_norm = normalize_constraint_expression (expr, /*diag=*/true);
-
-  /* Build the constraint, saving its two normalizations as its type.  */
-  tree r = build1 (NESTED_REQ, build_tree_list (diag_norm, norm), expr);
+  /* Build the requirement, saving the set of in-scope template
+     parameters as its type.  */
+  tree r = build1 (NESTED_REQ, current_template_parms, expr);
   SET_EXPR_LOCATION (r, loc);
   return r;
 }
@@ -3358,31 +3537,6 @@ check_function_concept (tree fn)
   return NULL_TREE;
 }
 
-
-// Check that a constrained friend declaration function declaration,
-// FN, is admissible. This is the case only when the declaration depends
-// on template parameters and does not declare a specialization.
-void
-check_constrained_friend (tree fn, tree reqs)
-{
-  if (fn == error_mark_node)
-    return;
-  gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
-
-  // If there are not constraints, this cannot be an error.
-  if (!reqs)
-    return;
-
-  // Constrained friend functions that don't depend on template
-  // arguments are effectively meaningless.
-  if (!uses_template_parms (TREE_TYPE (fn)))
-    {
-      error_at (location_of (fn),
-               "constrained friend does not depend on template parameters");
-      return;
-    }
-}
-
 /*---------------------------------------------------------------------------
                         Equivalence of constraints
 ---------------------------------------------------------------------------*/
@@ -3410,16 +3564,6 @@ equivalently_constrained (tree d1, tree d2)
                      Partial ordering of constraints
 ---------------------------------------------------------------------------*/
 
-/* Returns true when the constraints in A subsume those in B.  */
-
-bool
-subsumes_constraints (tree a, tree b)
-{
-  gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO);
-  gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO);
-  return subsumes (a, b);
-}
-
 /* Returns true when the constraints in CI strictly subsume
    the associated constraints of TMPL.  */
 
@@ -3517,30 +3661,39 @@ get_constraint_error_location (tree t)
 
 /* Emit a diagnostic for a failed trait.  */
 
-void
-diagnose_trait_expr (tree expr, tree map)
+static void
+diagnose_trait_expr (tree expr, tree args)
 {
   location_t loc = cp_expr_location (expr);
-  tree args = get_mapped_args (map);
 
   /* Build a "fake" version of the instantiated trait, so we can
      get the instantiated types from result.  */
   ++processing_template_decl;
-  expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false);
+  expr = tsubst_expr (expr, args, tf_none, NULL_TREE);
   --processing_template_decl;
 
   tree t1 = TRAIT_EXPR_TYPE1 (expr);
   tree t2 = TRAIT_EXPR_TYPE2 (expr);
+  if (t2 && TREE_CODE (t2) == TREE_VEC)
+    {
+      /* Convert the TREE_VEC of arguments into a TREE_LIST, since we can't
+        directly print a TREE_VEC but we can a TREE_LIST via the E format
+        specifier.  */
+      tree list = NULL_TREE;
+      for (tree t : tree_vec_range (t2))
+       list = tree_cons (NULL_TREE, t, list);
+      t2 = nreverse (list);
+    }
   switch (TRAIT_EXPR_KIND (expr))
     {
     case CPTK_HAS_NOTHROW_ASSIGN:
-      inform (loc, "  %qT is not %<nothrow%> copy assignable", t1);
+      inform (loc, "  %qT is not nothrow copy assignable", t1);
       break;
     case CPTK_HAS_NOTHROW_CONSTRUCTOR:
-      inform (loc, "  %qT is not %<nothrow%> default constructible", t1);
+      inform (loc, "  %qT is not nothrow default constructible", t1);
       break;
     case CPTK_HAS_NOTHROW_COPY:
-      inform (loc, "  %qT is not %<nothrow%> copy constructible", t1);
+      inform (loc, "  %qT is not nothrow copy constructible", t1);
       break;
     case CPTK_HAS_TRIVIAL_ASSIGN:
       inform (loc, "  %qT is not trivially copy assignable", t1);
@@ -3554,18 +3707,42 @@ diagnose_trait_expr (tree expr, tree map)
     case CPTK_HAS_TRIVIAL_DESTRUCTOR:
       inform (loc, "  %qT is not trivially destructible", t1);
       break;
+    case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
+      inform (loc, "  %qT does not have unique object representations", t1);
+      break;
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
       inform (loc, "  %qT does not have a virtual destructor", t1);
       break;
     case CPTK_IS_ABSTRACT:
       inform (loc, "  %qT is not an abstract class", t1);
       break;
+    case CPTK_IS_AGGREGATE:
+      inform (loc, "  %qT is not an aggregate", t1);
+      break;
+    case CPTK_IS_ARRAY:
+      inform (loc, "  %qT is not an array", t1);
+      break;
+    case CPTK_IS_ASSIGNABLE:
+      inform (loc, "  %qT is not assignable from %qT", t1, t2);
+      break;
     case CPTK_IS_BASE_OF:
       inform (loc, "  %qT is not a base of %qT", t1, t2);
       break;
+    case CPTK_IS_BOUNDED_ARRAY:
+      inform (loc, "  %qT is not a bounded array", t1);
+      break;
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONSTRUCTIBLE:
+      if (!t2)
+    inform (loc, "  %qT is not default constructible", t1);
+      else
+    inform (loc, "  %qT is not constructible from %qE", t1, t2);
+      break;
+    case CPTK_IS_CONVERTIBLE:
+      inform (loc, "  %qT is not convertible from %qE", t2, t1);
+      break;
     case CPTK_IS_EMPTY:
       inform (loc, "  %qT is not an empty class", t1);
       break;
@@ -3575,220 +3752,105 @@ diagnose_trait_expr (tree expr, tree map)
     case CPTK_IS_FINAL:
       inform (loc, "  %qT is not a final class", t1);
       break;
+    case CPTK_IS_FUNCTION:
+      inform (loc, "  %qT is not a function", t1);
+      break;
+    case CPTK_IS_LAYOUT_COMPATIBLE:
+      inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
+      break;
     case CPTK_IS_LITERAL_TYPE:
       inform (loc, "  %qT is not a literal type", t1);
       break;
+    case CPTK_IS_MEMBER_FUNCTION_POINTER:
+      inform (loc, "  %qT is not a member function pointer", t1);
+      break;
+    case CPTK_IS_MEMBER_OBJECT_POINTER:
+      inform (loc, "  %qT is not a member object pointer", t1);
+      break;
+    case CPTK_IS_MEMBER_POINTER:
+      inform (loc, "  %qT is not a member pointer", t1);
+      break;
+    case CPTK_IS_NOTHROW_ASSIGNABLE:
+      inform (loc, "  %qT is not nothrow assignable from %qT", t1, t2);
+      break;
+    case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
+      if (!t2)
+       inform (loc, "  %qT is not nothrow default constructible", t1);
+      else
+       inform (loc, "  %qT is not nothrow constructible from %qE", t1, t2);
+      break;
+    case CPTK_IS_NOTHROW_CONVERTIBLE:
+         inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
+      break;
+    case CPTK_IS_OBJECT:
+      inform (loc, "  %qT is not an object type", t1);
+      break;
+    case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
+      inform (loc, "  %qT is not pointer-interconvertible base of %qT",
+             t1, t2);
+      break;
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
-    case CPTK_IS_SAME_AS:
+    case CPTK_IS_REFERENCE:
+      inform (loc, "  %qT is not a reference", t1);
+      break;
+    case CPTK_IS_SAME:
       inform (loc, "  %qT is not the same as %qT", t1, t2);
       break;
+    case CPTK_IS_SCOPED_ENUM:
+      inform (loc, "  %qT is not a scoped enum", t1);
+      break;
     case CPTK_IS_STD_LAYOUT:
       inform (loc, "  %qT is not an standard layout type", t1);
       break;
     case CPTK_IS_TRIVIAL:
       inform (loc, "  %qT is not a trivial type", t1);
       break;
+    case CPTK_IS_TRIVIALLY_ASSIGNABLE:
+      inform (loc, "  %qT is not trivially assignable from %qT", t1, t2);
+      break;
+    case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+      if (!t2)
+       inform (loc, "  %qT is not trivially default constructible", t1);
+      else
+       inform (loc, "  %qT is not trivially constructible from %qE", t1, t2);
+      break;
+    case CPTK_IS_TRIVIALLY_COPYABLE:
+      inform (loc, "  %qT is not trivially copyable", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
-    default:
+    case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
+      inform (loc, "  %qT is not a reference that binds to a temporary "
+             "object of type %qT (direct-initialization)", t1, t2);
+      break;
+    case CPTK_REF_CONVERTS_FROM_TEMPORARY:
+      inform (loc, "  %qT is not a reference that binds to a temporary "
+             "object of type %qT (copy-initialization)", t1, t2);
+      break;
+    case CPTK_IS_DEDUCIBLE:
+      inform (loc, "  %qD is not deducible from %qT", t1, t2);
+      break;
+#define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
+    case CPTK_##CODE:
+#include "cp-trait.def"
+#undef DEFTRAIT_TYPE
+      /* Type-yielding traits aren't expressions.  */
       gcc_unreachable ();
+    /* We deliberately omit the default case so that when adding a new
+       trait we'll get reminded (by way of a warning) to handle it here.  */
     }
 }
 
-static tree
-diagnose_valid_expression (tree expr, tree args, tree in_decl)
-{
-  tree result = tsubst_expr (expr, args, tf_none, in_decl, false);
-  if (result != error_mark_node
-      && convert_to_void (result, ICV_STATEMENT, tf_none) != error_mark_node)
-    return result;
-
-  location_t loc = cp_expr_loc_or_input_loc (expr);
-  if (diagnosing_failed_constraint::replay_errors_p ())
-    {
-      /* Replay the substitution error.  */
-      inform (loc, "the required expression %qE is invalid, because", expr);
-      if (result == error_mark_node)
-       tsubst_expr (expr, args, tf_error, in_decl, false);
-      else
-       convert_to_void (result, ICV_STATEMENT, tf_error);
-    }
-  else
-    inform (loc, "the required expression %qE is invalid", expr);
-
-  return error_mark_node;
-}
-
-static tree
-diagnose_valid_type (tree type, tree args, tree in_decl)
-{
-  tree result = tsubst (type, args, tf_none, in_decl);
-  if (result != error_mark_node)
-    return result;
-
-  location_t loc = cp_expr_loc_or_input_loc (type);
-  if (diagnosing_failed_constraint::replay_errors_p ())
-    {
-      /* Replay the substitution error.  */
-      inform (loc, "the required type %qT is invalid, because", type);
-      tsubst (type, args, tf_error, in_decl);
-    }
-  else
-    inform (loc, "the required type %qT is invalid", type);
-
-  return error_mark_node;
-}
-
-static void
-diagnose_simple_requirement (tree req, tree args, tree in_decl)
-{
-  diagnose_valid_expression (TREE_OPERAND (req, 0), args, in_decl);
-}
-
-static void
-diagnose_compound_requirement (tree req, tree args, tree in_decl)
-{
-  tree expr = TREE_OPERAND (req, 0);
-  expr = diagnose_valid_expression (expr, args, in_decl);
-  if (expr == error_mark_node)
-    return;
-
-  location_t loc = cp_expr_loc_or_input_loc (expr);
-
-  /* Check the noexcept condition.  */
-  if (COMPOUND_REQ_NOEXCEPT_P (req) && !expr_noexcept_p (expr, tf_none))
-    inform (loc, "%qE is not %<noexcept%>", expr);
-
-  tree type = TREE_OPERAND (req, 1);
-  type = diagnose_valid_type (type, args, in_decl);
-  if (type == error_mark_node)
-    return;
-
-  if (type)
-    {
-      subst_info quiet (tf_none, in_decl);
-      subst_info noisy (tf_error, in_decl);
-
-      /* Check the expression against the result type.  */
-      if (tree placeholder = type_uses_auto (type))
-       {
-         if (!type_deducible_p (expr, type, placeholder, args, quiet))
-           {
-             tree orig_expr = TREE_OPERAND (req, 0);
-             if (diagnosing_failed_constraint::replay_errors_p ())
-               {
-                 inform (loc,
-                         "%qE does not satisfy return-type-requirement, "
-                         "because", orig_expr);
-                 /* Further explain the reason for the error.  */
-                 type_deducible_p (expr, type, placeholder, args, noisy);
-               }
-             else
-               inform (loc, "%qE does not satisfy return-type-requirement",
-                       orig_expr);
-           }
-       }
-      else if (!expression_convertible_p (expr, type, quiet))
-       {
-         tree orig_expr = TREE_OPERAND (req, 0);
-         if (diagnosing_failed_constraint::replay_errors_p ())
-           {
-             inform (loc, "cannot convert %qE to %qT because", orig_expr, type);
-             /* Further explain the reason for the error.  */
-             expression_convertible_p (expr, type, noisy);
-           }
-         else
-           inform (loc, "cannot convert %qE to %qT", orig_expr, type);
-       }
-    }
-}
-
-static void
-diagnose_type_requirement (tree req, tree args, tree in_decl)
-{
-  tree type = TREE_OPERAND (req, 0);
-  diagnose_valid_type (type, args, in_decl);
-}
+/* Diagnose a substitution failure in the atomic constraint T using ARGS.  */
 
 static void
-diagnose_nested_requirement (tree req, tree args)
-{
-  /* Quietly check for satisfaction first using the regular normal form.
-     We can elaborate details later if needed.  */
-  tree norm = TREE_VALUE (TREE_TYPE (req));
-  tree diag_norm = TREE_PURPOSE (TREE_TYPE (req));
-  sat_info info (tf_none, NULL_TREE);
-  tree result = satisfy_constraint (norm, args, info);
-  if (result == boolean_true_node)
-    return;
-
-  tree expr = TREE_OPERAND (req, 0);
-  location_t loc = cp_expr_location (expr);
-  if (diagnosing_failed_constraint::replay_errors_p ())
-    {
-      /* Replay the substitution error using the diagnostic normal form.  */
-      inform (loc, "nested requirement %qE is not satisfied, because", expr);
-      sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-      satisfy_constraint (diag_norm, args, noisy);
-    }
-  else
-    inform (loc, "nested requirement %qE is not satisfied", expr);
-
-}
-
-static void
-diagnose_requirement (tree req, tree args, tree in_decl)
-{
-  iloc_sentinel loc_s (cp_expr_location (req));
-  switch (TREE_CODE (req))
-    {
-    case SIMPLE_REQ:
-      return diagnose_simple_requirement (req, args, in_decl);
-    case COMPOUND_REQ:
-      return diagnose_compound_requirement (req, args, in_decl);
-    case TYPE_REQ:
-      return diagnose_type_requirement (req, args, in_decl);
-    case NESTED_REQ:
-      return diagnose_nested_requirement (req, args);
-    default:
-       gcc_unreachable ();
-    }
-}
-
-static void
-diagnose_requires_expr (tree expr, tree map, tree in_decl)
-{
-  local_specialization_stack stack (lss_copy);
-  tree parms = TREE_OPERAND (expr, 0);
-  tree body = TREE_OPERAND (expr, 1);
-  tree args = get_mapped_args (map);
-
-  cp_unevaluated u;
-  subst_info info (tf_warning_or_error, NULL_TREE);
-  tree vars = tsubst_constraint_variables (parms, args, info);
-  if (vars == error_mark_node)
-    return;
-
-  tree p = body;
-  while (p)
-    {
-      tree req = TREE_VALUE (p);
-      diagnose_requirement (req, args, in_decl);
-      p = TREE_CHAIN (p);
-    }
-}
-
-/* Diagnose a substitution failure in the atomic constraint T when applied
-   with the instantiated parameter mapping MAP.  */
-
-static void
-diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
+diagnose_atomic_constraint (tree t, tree args, tree result, sat_info info)
 {
   /* If the constraint is already ill-formed, we've previously diagnosed
      the reason. We should still say why the constraints aren't satisfied.  */
@@ -3812,10 +3874,15 @@ diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
   switch (TREE_CODE (expr))
     {
     case TRAIT_EXPR:
-      diagnose_trait_expr (expr, map);
+      diagnose_trait_expr (expr, args);
       break;
     case REQUIRES_EXPR:
-      diagnose_requires_expr (expr, map, info.in_decl);
+      gcc_checking_assert (info.diagnose_unsatisfaction_p ());
+      /* Clear in_decl before replaying the substitution to avoid emitting
+        seemingly unhelpful "in declaration ..." notes that follow some
+        substitution failure error messages.  */
+      info.in_decl = NULL_TREE;
+      tsubst_requires_expr (expr, args, info);
       break;
     default:
       if (!same_type_p (TREE_TYPE (result), boolean_type_node))
@@ -3868,7 +3935,7 @@ diagnosing_failed_constraint::replay_errors_p ()
 }
 
 /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
-   of T. Here, T can be either a constraint or a declaration.  */
+   of T.  Here, T and ARGS are as in constraints_satisfied_p.  */
 
 void
 diagnose_constraints (location_t loc, tree t, tree args)
@@ -3880,10 +3947,7 @@ diagnose_constraints (location_t loc, tree t, tree args)
 
   /* Replay satisfaction, but diagnose unsatisfaction.  */
   sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-  if (!args)
-    constraint_satisfaction_value (t, noisy);
-  else
-    constraint_satisfaction_value (t, args, noisy);
+  constraint_satisfaction_value (t, args, noisy);
 
   static bool suggested_p;
   if (concepts_diagnostics_max_depth_exceeded_p