On Tue, 2 Mar 2021, Jason Merrill wrote:
> On 3/2/21 11:45 AM, Patrick Palka wrote:
> > 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;
>
> This line needs a comment. OK with that change.
Thanks a lot. I committed the patch with the comment:
/* Clear in_decl before replaying the substitution to avoid emitting
seemingly unhelpful "in declaration ..." notes that follow some
substitution failure error messages. */
(Currently, if in_decl is nonempty here, then it points to the constrained
declaration to which this atom belongs. But this information is already neatly
emitted via the diagnosing_failed_constraint / maybe_print_constraint_context
machinery, so it'd be at best unnecessary to display it yet again via an "in
declaration" note. It's a minor issue, but the testcase
g++.dg/cpp2a/concepts-pr66844.C was sensitive to this.)
>
> > + 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
> >
>
>
Re: [PATCH 6/6] c++: Consolidate REQUIRES_EXPR evaluation/diagnostic routines
Patrick Palka via Gcc-patches Wed, 03 Mar 2021 09:10:38 -0800
- Re: [PATCH 4/4] c++: d... Jason Merrill via Gcc-patches
- Re: [PATCH 1/4] c++: Avoid building garbage ... Jason Merrill via Gcc-patches
- [PATCH 5/6] c++: Clean up normalization / sa... Patrick Palka via Gcc-patches
- Re: [PATCH 5/6] c++: Clean up normaliza... Jason Merrill via Gcc-patches
- Re: [PATCH 5/6] c++: Clean up norma... Patrick Palka via Gcc-patches
- Re: [PATCH 5/6] c++: Clean up n... Jason Merrill via Gcc-patches
- [PATCH 6/6] c++: Consolidate REQUIRES_EXPR e... Patrick Palka via Gcc-patches
- Re: [PATCH 6/6] c++: Consolidate REQUIR... Jason Merrill via Gcc-patches
- Re: [PATCH 6/6] c++: Consolidate RE... Patrick Palka via Gcc-patches
- Re: [PATCH 6/6] c++: Consolidat... Jason Merrill via Gcc-patches
- Re: [PATCH 6/6] c++: Consol... Patrick Palka via Gcc-patches
