On Thu, 3 Dec 2020, Jason Merrill wrote:

> On 12/3/20 9:24 AM, Patrick Palka wrote:
> > 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.  */
> 
> Could you write a bit more here about how noisy() and
> diagnose_unsatisfaction_p() are intended to be used?  What diagnostics are you
> looking for when noisy() is true and the other false?
> 
> As I understand it, the answer is, for constraints the non-bool and
> non-constant diagnostics from satisfy_atom; and for requires-expressions
> outside a template, any invalid types/expressions.  That's still kind of
> overloaded, but is probably OK with sufficient comments.  It took me a long
> time to work it out just now.
> 
> We get noisy+unsat from diagnose_constraints, and noisy-unsat from the replays
> inside
> constraint_satisfaction_value/evaluate_concept_check/tsubst_nested_requirement,
> and from the immediate checking of requires-exprs in
> cp_parser_requires_expression.  Right?

Yes, that's correct.  Another diagnostic that the noisy() flag would
control is one for when the satisfaction value of an atom is different
at different points in the program.

I've convinced myself to do away with the whole diagnose_requires_expr /
tsubst_requires_expr consolidation, since that part is just a pure
refactoring change and the added overloadedness of the flags is not
ideal.  This simplifies the patch considerably.

> 
> > +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);
> 
> As you mentioned on the meeting today, this doesn't work because we no longer
> have the necessary context for normalization; it was supposed to be sufficient
> to use the previously normalized form.  Perhaps we should pass true for 'diag'
> in finish_nested_requirement?

Hmm, that should work, but we might want to normalize twice at parse
time, once with diag=false and once with diag=true, and use the first
form during quiet satisfaction and the second during noisy satisfaction.
That way, we'll fully utilize the satisfaction cache during quiet
satisfaction (since normalize_atom return deduplicated atoms only when
diag=false).

> 
> Or if you have a different idea about preserving the template context, we
> could avoid normalizing at parse time.

Maybe we could maintain a hash_map from the NESTED_REQ tree to its
corresponding template context, and restore the context in
tsubst_nested_requirement during normalization?  On second thought, this
probably wouldn't play well with PCH, since the key to the map would be
a pointer value..

> 
> > +   }
> > +      else
> > +   inform (loc, "nested requirement %qE is not satisfied", expr);
> > +    }
> > +  else if (result == error_mark_node)
> >       {
> 
> Maybe some context message here, along the lines of "satisfaction of nested
> requirement %qE is ill-formed"?

Sounds good.  Would we want a similar context message in the other spots
where we replay satisfaction after obtaining an erroneous result, i.e.
in constraint_satisfaction_value and evaluate_concept_check?

Two patches incoming...

> 
> > -      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 }
> > 
> 
> 

Reply via email to