During satisfaction, the flag info.noisy() controls three things:
whether to diagnose fatal errors (such as the satisfaction value of an
atom being non-bool); whether to diagnose unsatisfaction; and whether to
bypass the satisfaction cache.

This flag turns out to be too coarse however, for sometimes we need to
diagnose fatal errors but not unsatisfaction, in particular when replaying
an erroneous satisfaction result from constraint_satisfaction_value,
evaluate_concept_check and tsubst_nested_requirement.

And we sometimes need to bypass the satisfaction cache but not diagnose
unsatisfaction, in particular when evaluating the branches of a
disjunction when info.noisy() is true.  Currently, satisfy_disjunction
first quietly evaluates each branch, but doing so causes satisfy_atom
to insert re-normalized atoms into the satisfaction cache when
diagnosing unsatisfaction of the overall constraint.  This is ultimately
the source of PR97093.

To that end, this patch adds the info.diagnose_unsatisfaction_p() flag
which refines the info.noisy() flag.  During satisfaction info.noisy()
now controls whether to diagnose fatal errors, and
info.diagnose_unsatisfaction_p() controls whether to additionally
diagnose unsatisfaction.  This enables us to address the above two
issues straightforwardly.

This flag refinement also allows us to fold the diagnose_foo_requirement
routines into the corresponding tsubst_foo_requirement ones.  Here, the
flags take on slightly different meanings: info.noisy() controls whether
to diagnose invalid types and expressions inside the requires-expression,
and info.diagnose_unsatisfaction_p() controls whether to diagnose the
overall unsatisfaction of the requires-expression.

Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
cmcstl2 and range-v3.  Does this look OK for trunk?

gcc/cp/ChangeLog:

        PR c++/97093
        * constraint.cc (struct sat_info): Define.
        (tsubst_valid_expression_requirement): Take a sat_info instead
        of subst_info.  Perform the substitution quietly first.  Fold in
        error-replaying code from diagnose_valid_expression.
        (tsubst_simple_requirement): Take a sat_info instead of
        subst_info.
        (tsubst_type_requirement_1): New.  Fold in error-replaying code
        from diagnose_valid_type.
        (tsubst_type_requirement): Use it. Take a sat_info instead of
        subst_info.
        (tsubst_compound_requirement): Likewise.  Fold in
        error-replaying code from diagnose_compound_requirement.
        (tsubst_nested_requirement): Take a sat_info instead of
        subst_info.  Perform the substitution quietly first.  Fold in
        error-replaying code from diagnose_nested_requirement.
        (tsubst_requirement): Take a sat_info instead of subst_info.
        (tsubst_requirement_body): Likewise.
        (tsubst_requires_expr): Split into two versions, one that takes
        a sat_info argument and another that takes a complain and
        in_decl argument.  Remove outdated documentation.  Document he
        effects of the sat_info argument.
        (tsubst_parameter_mapping): Take a sat_info instead of a
        subst_info.
        (satisfy_conjunction): Likewise.
        (satisfy_disjunction): Likewise.  Evaluate each branch with
        unsatisfaction diagnostics disabled rather than completely
        quietly, and short circuit when an erroneous branch is
        encountered.
        (satisfy_atom):  Take a sat_info instead of a subst_info.  Fix a
        comment.  Use diagnose_unsatisfaction_p() instead of noisy() to
        guard replaying of satisfaction failure.  Always check
        constantness quietly first and consistently return
        error_mark_node when the value is non-constant.
        (satisfy_constraint_r): Document the effects of the sat_info
        argument.  Take a sat_info instead of a subst_info.
        (satisfy_constraint): Take a sat_info instead of a subst_info.
        (satisfy_associated_constraints): Likewise.
        (satisfy_constraint_expression): Likewise.
        (satisfy_declaration_constraints): Likewise.
        (constraint_satisfaction_value): Likewise.  Adjust.  XXX
        (constraints_satisfied_p): Adjust.
        (evaluate_concept_check): Adjust.
        (diagnose_trait_expr): Make static.  Take a template args vector
        instead of a parameter mapping.
        (diagnose_atomic_constraint): Take a sat_info instead of a
        subst_info.  Adjust call to diagnose_trait_expr.  Call
        tsubst_requires_expr instead of diagnose_requires_expr.
        (diagnose_constraints): Adjust calls to
        constraint_satisfaction_value.
        (diagnose_valid_expression): Remove.
        (diagnose_valid_type): Likewise.
        (diagnose_simple_requirement): Likewise.
        (diagnose_compound_requirement): Likewise.
        (diagnose_type_requirement): Likewise.
        (diagnose_nested_requirement): Likewise.
        (diagnose_requirement): Likewise.
        (diagnose_requires_expr): Likewise.

gcc/testsuite/ChangeLog:

        PR c++/97093
        * g++.dg/concepts/diagnostic1.C: Adjust expected diagnostics now
        that we diagnose only the first failed requirement of a
        requires-expression.
        * g++.dg/concepts/pr94252.C: Verify we no longer issue a
        bogus satisfaction failure note when an erroneous atom is
        encountered during satisfaction.
        * g++.dg/cpp2a/concepts-nonbool3.C: New test that verifies we
        properly diagnose erroneous atoms within a disjunction during
        satisfaction.
        * g++.dg/cpp2a/concepts-pr66844.C: Expect an "in declaration"
        diagnostic now that in_decl is maintained when diagnosing an
        unsatisfied requires-expression.
        * g++.dg/cpp2a/concepts-pr97093.C: New test.
        * g++.dg/cpp2a/concepts-requires18.C: No longer expect a bogus
        satisfaction failure diagnostic when quietly evaluating the
        nested-requirement subst<void&> of a non-templated
        requires-expression.
        * g++.dg/cpp2a/concepts-requires21.C: Verify we no longer issue
        a bogus satisfaction failure note when quietly evaluating
        a nested-requirement of a non-templated requires-expression.
---
 gcc/cp/constraint.cc                          | 550 ++++++++----------
 gcc/testsuite/g++.dg/concepts/diagnostic1.C   |   2 +-
 gcc/testsuite/g++.dg/concepts/pr94252.C       |   1 +
 .../g++.dg/cpp2a/concepts-nonbool3.C          |   5 +
 gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C |   2 +-
 gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C |  31 +
 .../g++.dg/cpp2a/concepts-requires18.C        |   2 +-
 .../g++.dg/cpp2a/concepts-requires21.C        |   1 +
 8 files changed, 299 insertions(+), 295 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 7f02aa0a215..bf502953344 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -98,7 +98,30 @@ struct subst_info
   tree in_decl;
 };
 
-static tree satisfy_constraint (tree, tree, subst_info);
+/* Provides additional context for satisfaction.  */
+
+struct sat_info : subst_info
+{
+  sat_info (tsubst_flags_t cmp, tree in, bool diag_unsat = false)
+    : subst_info (cmp, in), diagnose_unsatisfaction (diag_unsat)
+  {
+    if (diagnose_unsatisfaction_p ())
+      gcc_assert (noisy ());
+  }
+
+  /* True if we should diagnose the cause of satisfaction failure.
+     Implies noisy().  */
+  bool
+  diagnose_unsatisfaction_p () const
+  {
+    return diagnose_unsatisfaction;
+  }
+
+  bool diagnose_unsatisfaction;
+};
+
+static tree satisfy_constraint (tree, tree, sat_info);
+static tree satisfy_constraint_expression (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.  */
@@ -1911,22 +1934,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, false);
+  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, false);
+         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, false);
+      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);
@@ -1935,13 +1980,40 @@ tsubst_simple_requirement (tree t, tree args, 
subst_info info)
   return finish_simple_requirement (EXPR_LOCATION (t), expr);
 }
 
+/* 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 finish_type_requirement (EXPR_LOCATION (t), type);
@@ -2018,7 +2090,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);
@@ -2026,13 +2098,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;
 
@@ -2044,36 +2123,83 @@ 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 finish_compound_requirement (EXPR_LOCATION (t),
                                      expr, type, noexcept_p);
 }
 
+/* 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)
 {
-  /* Ensure that we're in an evaluation context prior to satisfaction.  */
+  sat_info quiet (tf_none, info.in_decl);
   tree norm = TREE_TYPE (t);
-  tree result = satisfy_constraint (norm, args, info);
-  if (result == error_mark_node && info.quiet ())
+  tree result = satisfy_constraint (norm, args, quiet);
+  if (result == boolean_true_node)
+    return boolean_true_node;
+
+  if (result == boolean_false_node
+      && info.diagnose_unsatisfaction_p ())
+    {
+      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);
+         satisfy_constraint_expression (expr, args, info);
+       }
+      else
+       inform (loc, "nested requirement %qE is not satisfied", expr);
+    }
+  else if (result == error_mark_node)
     {
-      subst_info noisy (tf_warning_or_error, info.in_decl);
+      sat_info noisy (tf_warning_or_error, info.in_decl);
       satisfy_constraint (norm, args, noisy);
     }
-  if (result != boolean_true_node)
-    return error_mark_node;
-  return result;
+
+  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))
@@ -2096,7 +2222,7 @@ tsubst_requirement (tree t, tree args, subst_info info)
    substitution failures here result in ill-formed programs. */
 
 static tree
-tsubst_requirement_body (tree t, tree args, subst_info info)
+tsubst_requirement_body (tree t, tree args, sat_info info)
 {
   tree result = NULL_TREE;
   while (t)
@@ -2181,30 +2307,21 @@ 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 processing 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,
+   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);
-
   /* A requires-expression is an unevaluated context.  */
   cp_unevaluated u;
 
@@ -2216,7 +2333,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;
     }
 
@@ -2236,6 +2353,16 @@ tsubst_requires_expr (tree t, tree args,
   return boolean_true_node;
 }
 
+/* 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
    constraint record.  */
 
@@ -2256,7 +2383,7 @@ tsubst_constraint_info (tree t, tree args,
    if the substitution into arguments produces something ill-formed.  */
 
 static tree
-tsubst_parameter_mapping (tree map, tree args, subst_info info)
+tsubst_parameter_mapping (tree map, tree args, sat_info info)
 {
   if (!map)
     return NULL_TREE;
@@ -2326,7 +2453,7 @@ tsubst_parameter_mapping (tree map, tree args, subst_info 
info)
 tree
 tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree 
in_decl)
 {
-  return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl));
+  return tsubst_parameter_mapping (map, args, sat_info (complain, in_decl));
 }
 
 /*---------------------------------------------------------------------------
@@ -2508,12 +2635,12 @@ tsubst_constraint (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
   return expr;
 }
 
-static tree satisfy_constraint_r (tree, tree, subst_info info);
+static tree satisfy_constraint_r (tree, tree, sat_info info);
 
 /* Compute the satisfaction of a conjunction.  */
 
 static tree
-satisfy_conjunction (tree t, tree args, subst_info info)
+satisfy_conjunction (tree t, tree args, sat_info info)
 {
   tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info);
   if (lhs == error_mark_node || lhs == boolean_false_node)
@@ -2567,20 +2694,24 @@ collect_operands_of_disjunction (tree t, 
auto_vec<tree_pair> *operands)
 /* Compute the satisfaction of a disjunction.  */
 
 static tree
-satisfy_disjunction (tree t, tree args, subst_info info)
+satisfy_disjunction (tree t, tree args, sat_info info)
 {
-  /* Evaluate the operands quietly.  */
-  subst_info quiet (tf_none, NULL_TREE);
+  /* Evaluate each operand with unsatisfaction diagnostics disabled.  */
+  sat_info sub = info;
+  sub.diagnose_unsatisfaction = false;
 
-  /* Register the constraint for diagnostics, if needed.  */
-  diagnosing_failed_constraint failure (t, args, info.noisy ());
+  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, sub);
+  if (lhs == boolean_true_node || lhs == error_mark_node)
+    return lhs;
 
-  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, quiet);
-  if (lhs == boolean_true_node)
-    return boolean_true_node;
-  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet);
-  if (rhs != boolean_true_node && info.noisy ())
+  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, sub);
+  if (rhs == boolean_true_node || rhs == error_mark_node)
+    return rhs;
+
+  /* Diagnose the satisfaction failure in each branch.  */
+  if (info.diagnose_unsatisfaction_p ())
     {
+      diagnosing_failed_constraint failure (t, args, /*diag=*/true);
       cp_expr disj_expr = CONSTR_EXPR (t);
       inform (disj_expr.get_location (),
              "no operand of the disjunction is satisfied");
@@ -2601,7 +2732,8 @@ satisfy_disjunction (tree t, tree args, subst_info info)
            }
        }
     }
-  return rhs;
+
+  return boolean_false_node;
 }
 
 /* Ensures that T is a truth value and not (accidentally, as sometimes
@@ -2677,19 +2809,19 @@ get_mapped_args (tree map)
   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.  */
 
 static tree
-satisfy_atom (tree t, tree args, subst_info info)
+satisfy_atom (tree t, tree args, sat_info info)
 {
   satisfaction_cache cache (t, args, info.complain);
   if (tree r = cache.get ())
     return r;
 
   /* Perform substitution quietly.  */
-  subst_info quiet (tf_none, NULL_TREE);
+  sat_info quiet (tf_none, NULL_TREE);
 
   /* In case there is a diagnostic, we want to establish the context
      prior to printing errors.  If no errors occur, this context is
@@ -2700,9 +2832,9 @@ satisfy_atom (tree t, tree args, subst_info info)
   tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet);
   if (map == error_mark_node)
     {
-      /* If instantiation of the parameter mapping fails, the program
-         is ill-formed.  */
-      if (info.noisy())
+      /* If instantiation of the parameter mapping fails, the constraint is
+        not satisfied.  Replay the substitution.  */
+      if (info.diagnose_unsatisfaction_p ())
        tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info);
       return cache.save (boolean_false_node);
     }
@@ -2729,7 +2861,7 @@ satisfy_atom (tree t, tree args, subst_info info)
     {
       /* If substitution results in an invalid type or expression, the 
constraint
         is not satisfied. Replay the substitution.  */
-      if (info.noisy ())
+      if (info.diagnose_unsatisfaction_p ())
        tsubst_expr (expr, args, info.complain, info.in_decl, false);
       return cache.save (inst_cache.save (boolean_false_node));
     }
@@ -2747,17 +2879,18 @@ satisfy_atom (tree t, tree args, subst_info info)
     }
 
   /* Compute the value of the constraint.  */
-  if (info.noisy ())
-    result = cxx_constant_value (result);
-  else
+  result = maybe_constant_value (result, NULL_TREE,
+                                /*manifestly_const_eval=*/true);
+  if (!TREE_CONSTANT (result))
     {
-      result = maybe_constant_value (result, NULL_TREE,
-                                    /*manifestly_const_eval=*/true);
-      if (!TREE_CONSTANT (result))
-       result = error_mark_node;
+      if (info.noisy ())
+       cxx_constant_value (result);
+      return cache.save (inst_cache.save (error_mark_node));
     }
+
   result = satisfaction_value (result);
-  if (result == boolean_false_node && info.noisy ())
+  if (result == boolean_false_node
+      && info.diagnose_unsatisfaction_p ())
     diagnose_atomic_constraint (t, map, result, info);
 
   return cache.save (inst_cache.save (result));
@@ -2772,10 +2905,17 @@ satisfy_atom (tree t, tree args, subst_info info)
    set of template arguments that will be substituted into
    the expression, regardless of template parameters appearing
    withing. Whether a template argument is used in the atomic
-   constraint only matters for subsumption.  */
+   constraint only matters for subsumption.
+
+   When info.noisy() is true, diagnose errors that make the
+   program ill-formed.
+
+   When info.diagnose_unsatisfaction_p() is true and the overall
+   constraint is unsatisfied, additionally diagnose the cause of
+   satisfaction failure.  */
 
 static tree
-satisfy_constraint_r (tree t, tree args, subst_info info)
+satisfy_constraint_r (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
@@ -2796,7 +2936,7 @@ satisfy_constraint_r (tree t, tree args, subst_info info)
 /* Check that the normalized constraint T is satisfied for ARGS.  */
 
 static tree
-satisfy_constraint (tree t, tree args, subst_info info)
+satisfy_constraint (tree t, tree args, sat_info info)
 {
   auto_timevar time (TV_CONSTRAINT_SAT);
 
@@ -2814,7 +2954,7 @@ satisfy_constraint (tree t, tree args, subst_info info)
    value (either true, false, or error).  */
 
 static tree
-satisfy_associated_constraints (tree t, tree args, subst_info info)
+satisfy_associated_constraints (tree t, tree args, sat_info info)
 {
   /* If there are no constraints then this is trivially satisfied.  */
   if (!t)
@@ -2832,7 +2972,7 @@ satisfy_associated_constraints (tree t, tree args, 
subst_info info)
    satisfaction value. */
 
 static tree
-satisfy_constraint_expression (tree t, tree args, subst_info info)
+satisfy_constraint_expression (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
@@ -2861,12 +3001,12 @@ satisfy_constraint_expression (tree t, tree args, 
subst_info info)
 tree
 satisfy_constraint_expression (tree expr)
 {
-  subst_info info (tf_none, NULL_TREE);
+  sat_info info (tf_none, NULL_TREE);
   return satisfy_constraint_expression (expr, NULL_TREE, info);
 }
 
 static tree
-satisfy_declaration_constraints (tree t, subst_info info)
+satisfy_declaration_constraints (tree t, sat_info info)
 {
   gcc_assert (DECL_P (t));
   const tree saved_t = t;
@@ -2926,7 +3066,7 @@ satisfy_declaration_constraints (tree t, subst_info info)
 }
 
 static tree
-satisfy_declaration_constraints (tree t, tree args, subst_info info)
+satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
   /* Update the declaration for diagnostics.  */
   info.in_decl = t;
@@ -2951,9 +3091,8 @@ satisfy_declaration_constraints (tree t, tree args, 
subst_info info)
 }
 
 static tree
-constraint_satisfaction_value (tree t, tsubst_flags_t complain)
+constraint_satisfaction_value (tree t, sat_info info)
 {
-  subst_info info (complain, NULL_TREE);
   tree r;
   if (DECL_P (t))
     r = satisfy_declaration_constraints (t, info);
@@ -2961,26 +3100,31 @@ constraint_satisfaction_value (tree t, tsubst_flags_t 
complain)
     r = satisfy_constraint_expression (t, NULL_TREE, info);
   if (r == error_mark_node && info.quiet ()
       && !(DECL_P (t) && TREE_NO_WARNING (t)))
-      {
-       constraint_satisfaction_value (t, tf_warning_or_error);
-       if (DECL_P (t))
-         /* Avoid giving these errors again.  */
-         TREE_NO_WARNING (t) = true;
-      }
+    {
+      /* 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;
 }
 
 static tree
-constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain)
+constraint_satisfaction_value (tree t, tree args, sat_info info)
 {
-  subst_info info (complain, NULL_TREE);
   tree r;
   if (DECL_P (t))
     r = satisfy_declaration_constraints (t, args, info);
   else
     r = satisfy_constraint_expression (t, args, info);
   if (r == error_mark_node && info.quiet ())
-    constraint_satisfaction_value (t, args, tf_warning_or_error);
+    {
+      /* Replay the error with re-normalized requirements.  */
+      sat_info noisy (tf_warning_or_error, info.in_decl);
+      constraint_satisfaction_value (t, args, noisy);
+    }
   return r;
 }
 
@@ -2993,7 +3137,8 @@ constraints_satisfied_p (tree t)
   if (!flag_concepts)
     return true;
 
-  return constraint_satisfaction_value (t, tf_none) == boolean_true_node;
+  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
@@ -3005,7 +3150,8 @@ constraints_satisfied_p (tree t, tree args)
   if (!flag_concepts)
     return true;
 
-  return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node;
+  sat_info quiet (tf_none, NULL_TREE);
+  return constraint_satisfaction_value (t, args, quiet) == boolean_true_node;
 }
 
 /* Evaluate a concept check of the form C<ARGS>. This is only used for the
@@ -3020,14 +3166,14 @@ evaluate_concept_check (tree check, tsubst_flags_t 
complain)
   gcc_assert (concept_check_p (check));
 
   /* Check for satisfaction without diagnostics.  */
-  subst_info quiet (tf_none, NULL_TREE);
+  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.  */
-    subst_info noisy (tf_warning_or_error, NULL_TREE);
-    satisfy_constraint_expression (check, NULL_TREE, noisy);
-  }
+    {
+      /* 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;
 }
 
@@ -3300,11 +3446,10 @@ 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.  */
@@ -3384,193 +3529,11 @@ diagnose_trait_expr (tree expr, tree map)
     }
 }
 
-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);
-}
-
-static void
-diagnose_nested_requirement (tree req, tree args)
-{
-  /* Quietly check for satisfaction first. We can elaborate details
-     later if needed.  */
-  tree norm = TREE_TYPE (req);
-  subst_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.  */
-      inform (loc, "nested requirement %qE is not satisfied, because", expr);
-      subst_info noisy (tf_warning_or_error, NULL_TREE);
-      satisfy_constraint_expression (expr, 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 map, 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.  */
@@ -3591,13 +3554,15 @@ diagnose_atomic_constraint (tree t, tree map, tree 
result, subst_info info)
   /* Generate better diagnostics for certain kinds of expressions.  */
   tree expr = ATOMIC_CONSTR_EXPR (t);
   STRIP_ANY_LOCATION_WRAPPER (expr);
+  tree args = get_mapped_args (map);
   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 ());
+      tsubst_requires_expr (expr, args, info);
       break;
     default:
       if (!same_type_p (TREE_TYPE (result), boolean_type_node))
@@ -3660,11 +3625,12 @@ diagnose_constraints (location_t loc, tree t, tree args)
   if (concepts_diagnostics_max_depth == 0)
     return;
 
-  /* Replay satisfaction, but diagnose errors.  */
+  /* Replay satisfaction, but diagnose unsatisfaction.  */
+  sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
   if (!args)
-    constraint_satisfaction_value (t, tf_warning_or_error);
+    constraint_satisfaction_value (t, noisy);
   else
-    constraint_satisfaction_value (t, args, tf_warning_or_error);
+    constraint_satisfaction_value (t, args, noisy);
 
   static bool suggested_p;
   if (concepts_diagnostics_max_depth_exceeded_p
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic1.C 
b/gcc/testsuite/g++.dg/concepts/diagnostic1.C
index 29c78c4c730..23bd592411e 100644
--- a/gcc/testsuite/g++.dg/concepts/diagnostic1.C
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic1.C
@@ -8,7 +8,7 @@ concept bool SameAs = __is_same_as(T, U);
 template <class T>
 concept bool R1 = requires (T& t) { // { dg-message "in requirements" }
   { t.begin() } -> T;          // { dg-error "no match" }
-  { t.end() } -> SameAs<T*>;   // { dg-message "does not satisfy" }
+  { t.end() } -> SameAs<T*>;
 };
 
 template <class T>
diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C 
b/gcc/testsuite/g++.dg/concepts/pr94252.C
index 56ce5f88bad..b0457037ede 100644
--- a/gcc/testsuite/g++.dg/concepts/pr94252.C
+++ b/gcc/testsuite/g++.dg/concepts/pr94252.C
@@ -16,6 +16,7 @@ static_assert(requires(S o, int i) {
 
 template<typename T>
   concept c = requires (T t) { requires (T)5; }; // { dg-error "has type 
.int." }
+// { dg-bogus "not satisfied" "" { target *-*-* } .-1 }
 
 int
 foo()
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
new file mode 100644
index 00000000000..2a2af54847b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++20 } }
+
+template <auto V> concept C = false || V || false; // { dg-error "has type 
'int'" }
+template <auto V> int f() requires C<V>;
+int a = f<0>(); // { dg-error "no match" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
index afeee5927e5..efd9f035b20 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
@@ -11,6 +11,6 @@ concept C = requires (T t) {  // { dg-error "invalid 
parameter|in requirements" }
 
 template <typename T>
   requires C<T>
-constexpr bool is_c() { return true; }
+constexpr bool is_c() { return true; } // { dg-error "in declaration" }
 
 static_assert(is_c<void>(), ""); // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
new file mode 100644
index 00000000000..d9800159d70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
@@ -0,0 +1,31 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fconcepts-diagnostics-depth=3 
--param=hash-table-verification-limit=10000" }
+
+template <typename T>
+concept C =  requires (T t)
+{
+  requires t.some_const < 2 || requires { t.some_fn (); };
+};
+
+template <unsigned, unsigned>
+struct c
+{};
+
+template <typename T>
+concept P = requires (T t, c <0, 1> v) { { t (v) }; }; // { dg-error "no 
match" }
+
+template <P auto, P auto ...>
+struct m
+{
+  constexpr auto operator () (C auto) const
+  {};
+};
+
+struct pc
+{
+  constexpr auto operator () (C auto) const
+  {};
+};
+
+constexpr auto cc = pc {};
+constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
index a9b7720cc6c..9e45c586917 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
@@ -4,7 +4,7 @@ template<typename T>
 concept integer = __is_same_as(T, int);
 
 template<typename T>
-concept subst = requires (T x) { requires true; }; // { dg-error "parameter 
type .void." }
+concept subst = requires (T x) { requires true; };
 
 template<typename T>
 concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message 
"in requirements" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
index bc38b893c68..8aead2fe2c5 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
@@ -5,3 +5,4 @@ template<typename T, typename U>
 constexpr bool is_same_v = __is_same (T, U);
 
 static_assert(is_same_v<bool, decltype(requires { requires false; })>);
+// { dg-bogus "evaluated to 'false" "" { target *-*-* } .-1 }
-- 
2.29.2.404.ge67fbf927d

Reply via email to