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