[PATCH 6/6] c++: Consolidate REQUIRES_EXPR evaluation/diagnostic routines

Patrick Palka ppalka@redhat.com
Tue Mar 2 16:45:16 GMT 2021


On Mon, 1 Mar 2021, Jason Merrill wrote:

> On 2/28/21 12:59 PM, Patrick Palka wrote:
> > This folds the diagnose_requires_expr routines into the corresponding
> > tsubst_requires_expr ones.  This is achieved by making the latter
> > routines take a sat_info instead of a subst_info, and assigning the
> > appropriate meanings to the flags sat_info::noisy and
> > sat_info::diagnose_unsatisfaction_p during tsubst_requires_expr:
> > info.noisy() controls whether to diagnose invalid types and expressions
> > inside the requires-expression, and info.diagnose_unsatisfaction_p()
> > controls whether to diagnose why the requires-expression evaluates to
> > false.
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* constraint.cc (struct sat_info): Document the different
> > 	meanings of noisy() and diagnose_unsatisfaction_p() during
> > 	satisfaction and requires-expression evaluation.
> > 	(tsubst_valid_expression_requirement): Take a sat_info instead
> > 	of a 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 a
> > 	subst_info.
> > 	(tsubst_type_requirement_1): New.  Fold in error-replaying code
> > 	from diagnose_valid_type.
> > 	(tsubst_type_requirement): Use the above.  Take a sat_info
> > 	instead of a subst_info.
> > 	(tsubst_compound_requirement): Likewise.  Fold in
> > 	error-replaying code from diagnose_compound_requirement.
> > 	(tsubst_nested_requirement): Take a sat_info instead of a
> > 	subst_info.  Fold in error-replaying code from
> > 	diagnose_nested_requirement.
> > 	(tsubst_requirement): Take a sat_info instead of a subst_info.
> > 	(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 the
> > 	effects of the sat_info argument.
> > 	(diagnose_trait_expr): Make static.  Take a template argument
> > 	vector instead of a parameter mapping.
> > 	(diagnose_valid_expression): Remove.
> > 	(diagnose_valid_type): Remove.
> > 	(diagnose_simple_requirement): Remove.
> > 	(diagnose_compound_requirement): Remove.
> > 	(diagnose_type_requirement): Remove.
> > 	(diagnose_nested_requirement): Remove.
> > 	(diagnose_requirement): Remove.
> > 	(diagnose_requires_expr): Remove.
> > 	(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): Call tsubst_requires_expr instead of
> > 	diagnose_requires_expr.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/concepts/diagnostic1.C: Adjust expected diagnostics
> > 	now that we diagnose only the first failed requirement of a
> > 	requires-expression.
> > ---
> >   gcc/cp/constraint.cc                        | 416 +++++++++-----------
> >   gcc/testsuite/g++.dg/concepts/diagnostic1.C |   2 +-
> >   2 files changed, 179 insertions(+), 239 deletions(-)
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index cf319b34da0..31f32c25dfe 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -100,17 +100,30 @@ 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 entrypoint
> > for
> > -   which we set noisy-unsat is the replay inside
> > constraint_satisfaction_value.
> > -   From 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 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 explain why
> > +      the requires-expression evaluates to false.
> > +    - We enter tsubst_requires_expr with noisy+unsat from
> > diagnose_constraints
> > +      and from diagnose_atomic_constraint.
> > +    - 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
> >   {
> > @@ -1926,22 +1939,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);
> > @@ -1950,13 +1985,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;
> > @@ -2013,7 +2076,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);
> > @@ -2021,13 +2084,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;
> >   @@ -2039,29 +2109,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)
> >   {
> >     sat_info quiet (tf_none, info.in_decl);
> >     tree result = constraint_satisfaction_value (t, args, quiet);
> > -  if (result != boolean_true_node)
> > -    return error_mark_node;
> > -  return boolean_true_node;
> > +  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);
> > +	  constraint_satisfaction_value (t, args, info);
> > +	}
> > +      else
> > +	inform (loc, "nested requirement %qE is not satisfied", expr);
> > +    }
> > +
> > +  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))
> > @@ -2151,30 +2268,22 @@ 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);
> > -
> >     /* A requires-expression is an unevaluated context.  */
> >     cp_unevaluated u;
> >   @@ -2186,7 +2295,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;
> >       }
> >   @@ -2207,6 +2316,16 @@ tsubst_requires_expr (tree t, tree args,
> >     return boolean_true_node;
> >   }
> >   +/* 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
> >      constraint record.  */
> >   @@ -2790,7 +2909,7 @@ 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.  */
> >   @@ -3440,11 +3559,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.  */
> > @@ -3524,192 +3642,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.  */
> > -  sat_info quiet (tf_none, NULL_TREE);
> > -  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
> > -  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 with re-normalized requirements.  */
> > -      inform (loc, "nested requirement %qE is not satisfied, because",
> > expr);
> > -
> > -      sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
> > -      satisfy_nondeclaration_constraints (req, 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.
> > */
> > @@ -3730,13 +3667,16 @@ 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 ());
> > +      info.in_decl = NULL_TREE;
> > +      tsubst_requires_expr (expr, args, info);
> >         break;
> >       default:
> >         if (!same_type_p (TREE_TYPE (result), boolean_type_node))
> > @@ -3807,7 +3747,7 @@ diagnose_constraints (location_t loc, tree t, tree
> > args)
> >       {
> >         gcc_assert (!args);
> >         ++current_constraint_diagnosis_depth;
> > -      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
> > +      tsubst_requires_expr (t, /*args=*/NULL_TREE, noisy);
> >         --current_constraint_diagnosis_depth;
> >       }
> >     else
> > 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*>;
> 
> Are we no longer giving a message for this line?  That seems like a diagnostic
> quality regression.

This happens because diagnose_requires_expr didn't short-circuit its
processing of requirements upon seeing a failed requirement, and this
behavior got lost when it was merged with tsubst_requires_expr, which
does short-circuit.  I wasn't sure if we wanted to keep this behavior or
not :)

The below restores the previous non-short-circuiting behavior of
diagnose_requires_expr inside tsubst_requires_expr, and addresses the
TODO added by v2 of patch5/6.

-- >8 --

Subject: [PATCH] c++: Unify REQUIRES_EXPR evaluation / diagnostic routines

This folds the diagnose_requires_expr routines into the corresponding
tsubst_requires_expr ones.  This is achieved by making the latter
routines take a sat_info instead of a subst_info, and assigning the
appropriate meanings to the flags sat_info::noisy and
sat_info::diagnose_unsatisfaction_p during tsubst_requires_expr:
info.noisy() controls whether to diagnose invalid types and expressions
inside the requirements, and info.diagnose_unsatisfaction_p() controls
whether to additionally diagnose why the requires-expression evaluates
to false.

gcc/cp/ChangeLog:

	* constraint.cc (struct sat_info): Document the different
	meanings of noisy() and diagnose_unsatisfaction_p() during
	satisfaction and requires-expression evaluation.
	(tsubst_valid_expression_requirement): Take a sat_info instead
	of a 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 a
	subst_info.
	(tsubst_type_requirement_1): New.  Fold in error-replaying code
	from diagnose_valid_type.
	(tsubst_type_requirement): Use the above.  Take a sat_info
	instead of a subst_info.
	(tsubst_compound_requirement): Likewise.  Fold in
	error-replaying code from diagnose_compound_requirement.
	(tsubst_nested_requirement): Take a sat_info instead of a
	subst_info.  Fold in error-replaying code from
	diagnose_nested_requirement.
	(tsubst_requirement): Take a sat_info instead of a subst_info.
	(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 the
	effects of the sat_info argument.  Don't short-circuit
	processing of requirements when diagnosing unsatisfaction,
	mirroring diagnose_requires_expr.
	(satisfy_nondeclaration_constraint) <case REQUIRES_EXPR>: Remove
	assert, and se the three-parameter version of tsubst_requires_expr.
	(diagnose_trait_expr): Make static.  Take a template argument
	vector instead of a parameter mapping.
	(diagnose_valid_expression): Remove.
	(diagnose_valid_type): Remove.
	(diagnose_simple_requirement): Remove.
	(diagnose_compound_requirement): Remove.
	(diagnose_type_requirement): Remove.
	(diagnose_nested_requirement): Remove.
	(diagnose_requirement): Remove.
	(diagnose_requires_expr): Remove.
	(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): Remove special casing of REQUIRES_EXPR
	and just always call constraint_satisfaction_value.
---
 gcc/cp/constraint.cc | 444 ++++++++++++++++++-------------------------
 1 file changed, 189 insertions(+), 255 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 0949788aa29..b2d25f3c232 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -100,17 +100,30 @@ 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 entrypoint for
-   which we set noisy-unsat is the replay inside constraint_satisfaction_value.
-   From 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_constraints
+      and from diagnose_atomic_constraint.
+    - 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
 {
@@ -1926,22 +1939,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);
@@ -1950,13 +1985,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;
@@ -2013,7 +2076,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);
@@ -2021,13 +2084,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;
 
@@ -2039,29 +2109,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)
 {
   sat_info quiet (tf_none, info.in_decl);
   tree result = constraint_satisfaction_value (t, args, quiet);
-  if (result != boolean_true_node)
-    return error_mark_node;
-  return boolean_true_node;
+  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);
+	  constraint_satisfaction_value (t, args, info);
+	}
+      else
+	inform (loc, "nested requirement %qE is not satisfied", expr);
+    }
+
+  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))
@@ -2151,30 +2268,22 @@ 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);
-
   /* A requires-expression is an unevaluated context.  */
   cp_unevaluated u;
 
@@ -2186,7 +2295,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;
     }
 
@@ -2197,14 +2306,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
@@ -2790,7 +2915,7 @@ 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.  */
 
@@ -2976,14 +3101,10 @@ satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
   /* Handle REQUIRES_EXPR directly, bypassing satisfaction.  */
   if (TREE_CODE (t) == REQUIRES_EXPR)
     {
-      /* TODO: Remove this assert and the special casing of REQUIRES_EXPRs
-	 from diagnose_constraints once we merge tsubst_requires_expr and
-	 diagnose_requires_expr.  */
-      gcc_assert (!info.diagnose_unsatisfaction_p ());
       auto ovr = make_temp_override (current_constraint_diagnosis_depth);
       if (info.noisy ())
 	++current_constraint_diagnosis_depth;
-      return tsubst_requires_expr (t, args, info.complain, info.in_decl);
+      return tsubst_requires_expr (t, args, info);
     }
 
   /* Get the normalized constraints.  */
@@ -3466,11 +3587,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.  */
@@ -3550,192 +3670,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.  */
-  sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
-  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 with re-normalized requirements.  */
-      inform (loc, "nested requirement %qE is not satisfied, because", expr);
-
-      sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-      satisfy_nondeclaration_constraints (req, 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.  */
@@ -3756,13 +3695,16 @@ 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 ());
+      info.in_decl = NULL_TREE;
+      tsubst_requires_expr (expr, args, info);
       break;
     default:
       if (!same_type_p (TREE_TYPE (result), boolean_type_node))
@@ -3827,15 +3769,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 (TREE_CODE (t) == REQUIRES_EXPR)
-    {
-      gcc_assert (!args);
-      ++current_constraint_diagnosis_depth;
-      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
-      --current_constraint_diagnosis_depth;
-    }
-  else
-    constraint_satisfaction_value (t, args, noisy);
+  constraint_satisfaction_value (t, args, noisy);
 
   static bool suggested_p;
   if (concepts_diagnostics_max_depth_exceeded_p
-- 
2.31.0.rc0.75.gec125d1bc1



More information about the Gcc-patches mailing list