Message ID | 20210228175838.1286138-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | [1/4] c++: Avoid building garbage trees from tsubst_requires_expr | expand |
On 2/28/21 12:58 PM, Patrick Palka wrote: > This patch mostly performs some straightforward refactoring: > > - Renamed satisfy_constraint to satisfy_normalized_constraints > - Renamed the three-parameter version of satisfy_constraint_expression > to satisfy_nondeclaration_constraints > - Removed normalize_(non)?template_requirements > - Removed satisfy_associated_constraints (and made its callers > check for dependent template args sooner, before normalization) > - Removed the tsubst_flags_t parameter of evaluate_concept_check > - Combined the two versions of constraint_satisfaction_value > - Combined the two versions of constraint_satisfied_p > > Additionally, this patch removes the handling of bare > constraint-expressions from satisfy_nondeclaration_constraints, and > hence constraints_satisfied_p and constraint_satisfaction_value now only > take things that carry their own template information needed for > normalization. In practice, this only means it's no longer possible to > evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this > patch adjusts the affected callers to instead use tsubst_requires_expr. It's probably better to have a different entry point than tsubst_requires_expr for callers that have nothing to do with templates. Whether that's a one-line wrapper for the call to tsubst_requires_expr ("evaluate_requires_expr"?) or calling tsubst_requires_expr from satisfy_nondeclaration_constraints, I don't have an opinion either way. > For convenience, the function diagnose_constraints continues to accept > REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr > directly. This might argue for the latter choice above. > (That we used to evaluate REQUIRES_EXPR via satisfaction might even be a > correctness issue: since we cache satisfaction in special ways that don't > apply to regular evaluation, going through satisfaction could in theory > cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't > have.) > gcc/cp/ChangeLog: > > * constexpr.c (cxx_eval_call_expression): Adjust call to > evaluate_concept_check. > (cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use > tsubst_requires_expr instead of satisfy_constraint_expression. > <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. > * constraint.cc (struct sat_info): Adjust comment about which > satisfaction entrypoints use noisy-unsat. > (normalize_template_requirements): Remove (and adjust callers > appropriately). > (normalize_nontemplate_requirements): Likewise. > (tsubst_nested_requirement): Use constraint_satisfaction_value > instead of satisfy_constraint_expression, which'll do the > noisy replaying of ill-formed quiet satisfaction for us. > (decl_satisfied_cache): Adjust comment. > (satisfy_constraint): Rename to ... > (satisfy_normalized_constraints): ... this. > (satisfy_associated_constraints): Remove (and make its > callers check for dependent arguments). > (satisfy_constraint_expression): Rename to ... > (satisfy_nondeclaration_constraints): ... this. Assert that > 'args' is empty when 't' is a concept-id. Removing handling > bare constraint-expressions. Adjust comment accordingly. > (satisfy_declaration_constraints): Assert in the two-parameter > version that 't' is not a TEMPLATE_DECL. Adjust following > removal of normalize_(non)?template_requirements and > satisfy_asociated_constraints. > (constraint_satisfaction_value): Combine the two- and > three-parameter versions in the natural way. > (constraints_satisfied_p): Combine the one- and two-parameter > versions in the natural way. Improve documentation. > (evaluate_concept_check): Remove 'complain' parameter. Use > constraint_satisfaction_value instead of > satisfy_constraint_expression. > (diagnose_nested_requirement): Adjust following renaming of > satisfy_constraint_expression. > (diagnose_constraints): Handle REQUIRES_EXPR by going through > diagnose_requires_expr directly instead of treating it as a > constraint-expression. Improve documentation. > * cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call > to evaluate_concept_check. > <case REQUIRES_EXPR>: Use tsubst_requires_expr instead of > constraints_satisfied_p. > <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. > * cp-tree.h (evaluate_concept_check): Remove tsubst_flag_t > parameter. > (satisfy_constraint_expression): Remove declaration. > (constraints_satisfied_p): Remove one-parameter declaration. > Add a default argument to the two-parameter declaration. > * cvt.c (convert_to_void): Adjust call to > evaluate_concept_check. > --- > gcc/cp/constexpr.c | 6 +- > gcc/cp/constraint.cc | 210 ++++++++++++++++--------------------------- > gcc/cp/cp-gimplify.c | 7 +- > gcc/cp/cp-tree.h | 6 +- > gcc/cp/cvt.c | 2 +- > 5 files changed, 85 insertions(+), 146 deletions(-) > > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c > index cd0a68e9fd6..f940e3e5985 100644 > --- a/gcc/cp/constexpr.c > +++ b/gcc/cp/constexpr.c > @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > { > /* Handle concept checks separately. */ > if (concept_check_p (t)) > - return evaluate_concept_check (t, tf_warning_or_error); > + return evaluate_concept_check (t); > > location_t loc = cp_expr_loc_or_input_loc (t); > tree fun = get_function_named_in_call (t); > @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, > '!requires (T t) { ... }' which is not transformed into > a constraint. */ > if (!processing_template_decl) > - return satisfy_constraint_expression (t); > + return tsubst_requires_expr (t, NULL_TREE, tf_none, NULL_TREE); > else > *non_constant_p = true; > return t; > @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, > > if (!processing_template_decl > && !uid_sensitive_constexpr_evaluation_p ()) > - r = evaluate_concept_check (t, tf_warning_or_error); > + r = evaluate_concept_check (t); > else > *non_constant_p = true; > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 2b61ad8d9ea..cf319b34da0 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -107,10 +107,9 @@ struct subst_info > a constraint is not satisfied. > > The entrypoints to satisfaction for which we set noisy+unsat are > - diagnose_constraints and diagnose_nested_requirement. The entrypoints for > - which we set noisy-unsat are the replays inside constraint_satisfaction_value, > - evaluate_concept_check and tsubst_nested_requirement. In other entrypoints, > - e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags > + 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). */ > > struct sat_info : subst_info > @@ -133,7 +132,7 @@ struct sat_info : subst_info > bool diagnose_unsatisfaction; > }; > > -static tree satisfy_constraint_expression (tree, tree, sat_info); > +static tree constraint_satisfaction_value (tree, tree, sat_info); > > /* True if T is known to be some type other than bool. Note that this > is false for dependent types and errors. */ > @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false) > return norm; > } > > -/* Returns the normal form of TMPL's requirements. */ > - > -static tree > -normalize_template_requirements (tree tmpl, bool diag = false) > -{ > - return get_normalized_constraints_from_decl (tmpl, diag); > -} > - > -/* Returns the normal form of TMPL's requirements. */ > - > -static tree > -normalize_nontemplate_requirements (tree decl, bool diag = false) > -{ > - return get_normalized_constraints_from_decl (decl, diag); > -} > - > /* Normalize an EXPR as a constraint. */ > > static tree > @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info) > static tree > tsubst_nested_requirement (tree t, tree args, subst_info info) > { > - /* Perform satisfaction quietly first. */ > sat_info quiet (tf_none, info.in_decl); > - tree result = satisfy_constraint_expression (t, args, quiet); > - if (result == error_mark_node) > - { > - /* Replay the error. */ > - sat_info noisy (tf_warning_or_error, info.in_decl); > - satisfy_constraint_expression (t, args, noisy); > - } > + tree result = constraint_satisfaction_value (t, args, quiet); > if (result != boolean_true_node) > return error_mark_node; > return boolean_true_node; > @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry> > /* Cache the result of satisfy_atom. */ > static GTY((deletable)) hash_table<sat_hasher> *sat_cache; > > -/* Cache the result of constraint_satisfaction_value. */ > +/* Cache the result of satisfy_declaration_constraints. */ > static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; > > /* A tool used by satisfy_atom to help manage satisfaction caching and to > @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info) > /* Check that the normalized constraint T is satisfied for ARGS. */ > > static tree > -satisfy_constraint (tree t, tree args, sat_info info) > +satisfy_normalized_constraints (tree t, tree args, sat_info info) > { > auto_timevar time (TV_CONSTRAINT_SAT); > > @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info) > return satisfy_constraint_r (t, args, info); > } > > -/* Check the normalized constraints T against ARGS, returning a satisfaction > - value (either true, false, or error). */ > - > -static tree > -satisfy_associated_constraints (tree t, tree args, sat_info info) > -{ > - /* If there are no constraints then this is trivially satisfied. */ > - if (!t) > - return boolean_true_node; > - > - /* If any arguments depend on template parameters, we can't > - check constraints. Pretend they're satisfied for now. */ > - if (args && uses_template_parms (args)) > - return boolean_true_node; > - > - return satisfy_constraint (t, args, info); > -} > - > /* Return the normal form of the constraints on the placeholder 'auto' > type T. */ > > @@ -3005,19 +2963,20 @@ normalize_placeholder_type_constraints (tree t, bool diag) > return normalize_constraint_expression (constr, info); > } > > -/* Evaluate EXPR as a constraint expression using ARGS, returning a > - satisfaction value. */ > +/* Evaluate the constraints of T using ARGS, returning a satisfaction value. > + Here, T can be a concept-id, nested-requirement or placeholder 'auto'. */ > > static tree > -satisfy_constraint_expression (tree t, tree args, sat_info info) > +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info) > { > if (t == error_mark_node) > return error_mark_node; > > /* Get the normalized constraints. */ > tree norm; > - if (args == NULL_TREE && concept_check_p (t)) > + if (concept_check_p (t)) > { > + gcc_assert (!args); > tree id = unpack_concept_check (t); > args = TREE_OPERAND (id, 1); > tree tmpl = get_concept_check_template (id); > @@ -3032,11 +2991,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) > ninfo.initial_parms = TREE_TYPE (t); > norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo); > } > - else if (EXPR_P (t)) > - { > - norm_info ninfo (info.noisy () ? tf_norm : tf_none); > - norm = normalize_constraint_expression (t, ninfo); > - } > else if (is_auto (t)) > { > norm = normalize_placeholder_type_constraints (t, info.noisy ()); > @@ -3047,23 +3001,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) > gcc_unreachable (); > > /* Perform satisfaction. */ > - return satisfy_constraint (norm, args, info); > + return satisfy_normalized_constraints (norm, args, info); > } > > -/* Used only to evaluate requires-expressions during constant expression > - evaluation. */ > - > -tree > -satisfy_constraint_expression (tree expr) > -{ > - sat_info info (tf_none, NULL_TREE); > - return satisfy_constraint_expression (expr, NULL_TREE, info); > -} > +/* Evaluate the associated constraints of the template specialization T > + according to INFO, returning a satisfaction value. */ > > static tree > satisfy_declaration_constraints (tree t, sat_info info) > { > - gcc_assert (DECL_P (t)); > + gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL); > const tree saved_t = t; > > /* For inherited constructors, consider the original declaration; > @@ -3083,26 +3030,24 @@ satisfy_declaration_constraints (tree t, sat_info info) > if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t)) > return *result; > > - /* Get the normalized constraints. */ > - tree norm = NULL_TREE; > tree args = NULL_TREE; > if (tree ti = DECL_TEMPLATE_INFO (t)) > { > - tree tmpl = TI_TEMPLATE (ti); > - norm = normalize_template_requirements (tmpl, info.noisy ()); > - > /* The initial parameter mapping is the complete set of > template arguments substituted into the declaration. */ > args = TI_ARGS (ti); > if (inh_ctor_targs) > args = add_outermost_template_args (args, inh_ctor_targs); > - } > - else > - { > - /* These should be empty until we allow constraints on non-templates. */ > - norm = normalize_nontemplate_requirements (t, info.noisy ()); > + > + /* If any arguments depend on template parameters, we can't > + check constraints. Pretend they're satisfied for now. */ > + if (uses_template_parms (args)) > + return boolean_true_node; > } > > + /* Get the normalized constraints. */ > + tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); > + > unsigned ftc_count = vec_safe_length (failed_type_completions); > > tree result = boolean_true_node; > @@ -3111,7 +3056,7 @@ satisfy_declaration_constraints (tree t, sat_info info) > if (!push_tinst_level (t)) > return result; > push_access_scope (t); > - result = satisfy_associated_constraints (norm, args, info); > + result = satisfy_normalized_constraints (norm, args, info); > pop_access_scope (t); > pop_tinst_level (); > } > @@ -3134,6 +3079,10 @@ satisfy_declaration_constraints (tree t, sat_info info) > return result; > } > > +/* Evaluate the associated constraints of the template T using ARGS as the > + innermost set of template arguments and according to INFO, returning a > + satisfaction value. */ > + > static tree > satisfy_declaration_constraints (tree t, tree args, sat_info info) > { > @@ -3144,14 +3093,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > > args = add_outermost_template_args (t, args); > > + /* If any arguments depend on template parameters, we can't > + check constraints. Pretend they're satisfied for now. */ > + if (uses_template_parms (args)) > + return boolean_true_node; > + > tree result = boolean_true_node; > - if (tree norm = normalize_template_requirements (t, info.noisy ())) > + if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) > { > if (!push_tinst_level (t, args)) > return result; > tree pattern = DECL_TEMPLATE_RESULT (t); > push_access_scope (pattern); > - result = satisfy_associated_constraints (norm, args, info); > + result = satisfy_normalized_constraints (norm, args, info); > pop_access_scope (pattern); > pop_tinst_level (); > } > @@ -3159,62 +3113,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > return result; > } > > +/* A wrapper around satisfy_declaration_constraints and > + satisfy_nondeclaration_constraints which additionally replays > + quiet ill-formed satisfaction noisily, so that ill-formed > + satisfaction always gets diagnosed. */ > + > static tree > -constraint_satisfaction_value (tree t, sat_info info) > +constraint_satisfaction_value (tree t, tree args, sat_info info) > { > tree r; > if (DECL_P (t)) > - r = satisfy_declaration_constraints (t, info); > + { > + if (args) > + r = satisfy_declaration_constraints (t, args, info); > + else > + r = satisfy_declaration_constraints (t, info); > + } > else > - r = satisfy_constraint_expression (t, NULL_TREE, info); > + r = satisfy_nondeclaration_constraints (t, args, info); > if (r == error_mark_node && info.quiet () > && !(DECL_P (t) && TREE_NO_WARNING (t))) > { > - /* Replay the error with re-normalized requirements. */ > + /* Replay the error noisily. */ > sat_info noisy (tf_warning_or_error, info.in_decl); > - constraint_satisfaction_value (t, noisy); > - if (DECL_P (t)) > + constraint_satisfaction_value (t, args, noisy); > + if (DECL_P (t) && !args) > /* Avoid giving these errors again. */ > TREE_NO_WARNING (t) = true; > } > return r; > } > > -static tree > -constraint_satisfaction_value (tree t, tree args, sat_info info) > -{ > - tree r; > - if (DECL_P (t)) > - r = satisfy_declaration_constraints (t, args, info); > - else > - r = satisfy_constraint_expression (t, args, info); > - if (r == error_mark_node && info.quiet ()) > - { > - /* Replay the error with re-normalized requirements. */ > - sat_info noisy (tf_warning_or_error, info.in_decl); > - constraint_satisfaction_value (t, args, noisy); > - } > - return r; > -} > - > -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false > - otherwise, even in the case of errors. */ > +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE > + and false otherwise, even in the case of errors. > > -bool > -constraints_satisfied_p (tree t) > -{ > - if (!flag_concepts) > - return true; > - > - sat_info quiet (tf_none, NULL_TREE); > - return constraint_satisfaction_value (t, quiet) == boolean_true_node; > -} > - > -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE > - and false otherwise, even in the case of errors. */ > + Here, T can be: > + - a template declaration (in which case ARGS is an innermost template > + argument set for T) > + - a template specialization (in which case ARGS must be empty) > + - a concept-id (in which case ARGS must be empty) > + - a nested-requirement (in which case ARGS is a complete argument set) > + - a placeholder 'auto' (in which case ARGS is a complete argument set). */ > > bool > -constraints_satisfied_p (tree t, tree args) > +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */) > { > if (!flag_concepts) > return true; > @@ -3227,7 +3169,7 @@ constraints_satisfied_p (tree t, tree args) > evaluation of template-ids as id-expressions. */ > > tree > -evaluate_concept_check (tree check, tsubst_flags_t complain) > +evaluate_concept_check (tree check) > { > if (check == error_mark_node) > return error_mark_node; > @@ -3236,14 +3178,7 @@ evaluate_concept_check (tree check, tsubst_flags_t complain) > > /* Check for satisfaction without diagnostics. */ > sat_info quiet (tf_none, NULL_TREE); > - tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); > - if (result == error_mark_node && (complain & tf_error)) > - { > - /* Replay the error with re-normalized requirements. */ > - sat_info noisy (tf_warning_or_error, NULL_TREE); > - satisfy_constraint_expression (check, NULL_TREE, noisy); > - } > - return result; > + return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); > } > > /*--------------------------------------------------------------------------- > @@ -3709,7 +3644,7 @@ diagnose_nested_requirement (tree req, tree args) > { > /* Quietly check for satisfaction first. */ > sat_info quiet (tf_none, NULL_TREE); > - tree result = satisfy_constraint_expression (req, args, quiet); > + tree result = satisfy_nondeclaration_constraints (req, args, quiet); > if (result == boolean_true_node) > return; > > @@ -3721,7 +3656,7 @@ diagnose_nested_requirement (tree req, tree args) > inform (loc, "nested requirement %qE is not satisfied, because", expr); > > sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true); > - satisfy_constraint_expression (req, args, noisy); > + satisfy_nondeclaration_constraints (req, args, noisy); > } > else > inform (loc, "nested requirement %qE is not satisfied", expr); > @@ -3854,7 +3789,9 @@ diagnosing_failed_constraint::replay_errors_p () > } > > /* Emit diagnostics detailing the failure ARGS to satisfy the constraints > - of T. Here, T can be either a constraint or a declaration. */ > + of T. Here, T and ARGS are as in constraints_satisfied_p, except that T > + can also be a REQUIRES_EXPR (in which case ARGS is must be empty). The > + latter is used by finish_static_assert to diagnose a false REQUIRES_EXPR. */ > > void > diagnose_constraints (location_t loc, tree t, tree args) > @@ -3866,8 +3803,13 @@ 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 (!args) > - constraint_satisfaction_value (t, noisy); > + 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); > > diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c > index abb8a6ef078..64b1dc1b433 100644 > --- a/gcc/cp/cp-gimplify.c > +++ b/gcc/cp/cp-gimplify.c > @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) > normal functions. */ > if (concept_check_p (stmt)) > { > - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); > + *stmt_p = evaluate_concept_check (stmt); > * walk_subtrees = 0; > break; > } > @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) > > case REQUIRES_EXPR: > /* Emit the value of the requires-expression. */ > - *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt), > - boolean_type_node); > + *stmt_p = tsubst_requires_expr (stmt, NULL_TREE, tf_none, NULL_TREE); > *walk_subtrees = 0; > break; > > case TEMPLATE_ID_EXPR: > gcc_assert (concept_check_p (stmt)); > /* Emit the value of the concept check. */ > - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); > + *stmt_p = evaluate_concept_check (stmt); > walk_subtrees = 0; > break; > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 544e99538a4..995e13f4a6e 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -8123,10 +8123,8 @@ struct processing_constraint_expression_sentinel > extern bool processing_constraint_expression_p (); > > extern tree unpack_concept_check (tree); > -extern tree evaluate_concept_check (tree, tsubst_flags_t); > -extern tree satisfy_constraint_expression (tree); > -extern bool constraints_satisfied_p (tree); > -extern bool constraints_satisfied_p (tree, tree); > +extern tree evaluate_concept_check (tree); > +extern bool constraints_satisfied_p (tree, tree = NULL_TREE); > extern bool* lookup_subsumption_result (tree, tree); > extern bool save_subsumption_result (tree, tree, bool); > extern tree find_template_parameters (tree, tree); > diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c > index e809f0e4068..3f5467c8283 100644 > --- a/gcc/cp/cvt.c > +++ b/gcc/cp/cvt.c > @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) > /* Explicitly evaluate void-converted concept checks since their > satisfaction may produce ill-formed programs. */ > if (concept_check_p (expr)) > - expr = evaluate_concept_check (expr, tf_warning_or_error); > + expr = evaluate_concept_check (expr); > > if (VOID_TYPE_P (TREE_TYPE (expr))) > return expr; >
On Mon, 1 Mar 2021, Jason Merrill wrote: > On 2/28/21 12:58 PM, Patrick Palka wrote: > > This patch mostly performs some straightforward refactoring: > > > > - Renamed satisfy_constraint to satisfy_normalized_constraints > > - Renamed the three-parameter version of satisfy_constraint_expression > > to satisfy_nondeclaration_constraints > > - Removed normalize_(non)?template_requirements > > - Removed satisfy_associated_constraints (and made its callers > > check for dependent template args sooner, before normalization) > > - Removed the tsubst_flags_t parameter of evaluate_concept_check > > - Combined the two versions of constraint_satisfaction_value > > - Combined the two versions of constraint_satisfied_p > > > > Additionally, this patch removes the handling of bare > > constraint-expressions from satisfy_nondeclaration_constraints, and > > hence constraints_satisfied_p and constraint_satisfaction_value now only > > take things that carry their own template information needed for > > normalization. In practice, this only means it's no longer possible to > > evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this > > patch adjusts the affected callers to instead use tsubst_requires_expr. > > It's probably better to have a different entry point than tsubst_requires_expr > for callers that have nothing to do with templates. Whether that's a one-line > wrapper for the call to tsubst_requires_expr ("evaluate_requires_expr"?) or > calling tsubst_requires_expr from satisfy_nondeclaration_constraints, I don't > have an opinion either way. > > > For convenience, the function diagnose_constraints continues to accept > > REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr > > directly. > > This might argue for the latter choice above. Ah, I didn't consider the option of continuing to handle REQUIRES_EXPRs from satisfy_nondeclaration_constraints by evaluating them directly. Here's a patch that implements this. I opted to add a evaluate_requires_expr wrapper as well, since evaluate_requires_expr (t) is much more readable than constant_boolean_node (constraints_satisfied_p (t), boolean_type_node) (Note the added TODO in satisfy_nondeclaration_constraints, which'll be fixed in a new version of the REQUIRES_EXPR evaluation/diagnostic unification patch.) -- >8 -- Subject: [PATCH] c++: Clean up normalization / satisfaction routines This patch mostly performs some straightforward refactoring: - Renamed satisfy_constraint to satisfy_normalized_constraints - Renamed the three-parameter version of satisfy_constraint_expression to satisfy_nondeclaration_constraints - Removed normalize_(non)?template_requirements - Removed satisfy_associated_constraints (and made its callers check for dependent template args sooner, before normalization) - Removed the tsubst_flags_t parameter of evaluate_concept_check - Combined the two versions of constraint_satisfaction_value - Combined the two versions of constraint_satisfied_p Additionally, this patch removes the handling of general constraint-expressions from satisfy_nondeclaration_constraints, and hence constraints_satisfied_p and constraint_satisfaction_value now take only things that carry their own template information needed for normalization, and, as a special case, REQUIRES_EXPRs. But the latter now get evaluated directly via tsubst_requires_expr rather than going through satisfaction. (That we used to evaluate REQUIRES_EXPR via satisfaction might even be a correctness issue: since we cache satisfaction in special ways that don't apply to regular evaluation, going through satisfaction could in theory cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't have.) gcc/cp/ChangeLog: * constexpr.c (cxx_eval_call_expression): Adjust call to evaluate_concept_check. (cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use evaluate_requires_expression instead of satisfy_constraint_expression. <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. * constraint.cc (struct sat_info): Adjust comment about which satisfaction entrypoints use noisy-unsat. (normalize_template_requirements): Remove (and adjust callers appropriately). (normalize_nontemplate_requirements): Likewise. (tsubst_nested_requirement): Use constraint_satisfaction_value instead of satisfy_constraint_expression, which'll do the noisy replaying of ill-formed quiet satisfaction for us. (decl_satisfied_cache): Adjust comment. (satisfy_constraint): Rename to ... (satisfy_normalized_constraints): ... this. (satisfy_associated_constraints): Remove (and make its callers check for dependent arguments). (satisfy_constraint_expression): Rename to ... (satisfy_nondeclaration_constraints): ... this. Assert that 'args' is empty when 't' is a concept-id. Removing handling bare constraint-expressions, and handle REQUIRES_EXPRs specially. Adjust comment accordingly. (satisfy_declaration_constraints): Assert in the two-parameter version that 't' is not a TEMPLATE_DECL. Adjust following removal of normalize_(non)?template_requirements and satisfy_asociated_constraints. (constraint_satisfaction_value): Combine the two- and three-parameter versions in the natural way. (constraints_satisfied_p): Combine the one- and two-parameter versions in the natural way. Improve documentation. (evaluate_requires_expr): Define. (evaluate_concept_check): Remove 'complain' parameter. Use constraint_satisfaction_value instead of satisfy_constraint_expression. (diagnose_nested_requirement): Adjust following renaming of satisfy_constraint_expression. (diagnose_constraints): Handle REQUIRES_EXPR by going through diagnose_requires_expr directly instead of treating it as a constraint-expression. Improve documentation. * cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call to evaluate_concept_check. <case REQUIRES_EXPR>: Use evaluate_requires_expr instead of constraints_satisfied_p. <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. * cp-tree.h (evaluate_requires_expr): Declare. (evaluate_concept_check): Remove tsubst_flag_t parameter. (satisfy_constraint_expression): Remove declaration. (constraints_satisfied_p): Remove one-parameter declaration. Add a default argument to the two-parameter declaration. * cvt.c (convert_to_void): Adjust call to evaluate_concept_check. --- gcc/cp/constexpr.c | 6 +- gcc/cp/constraint.cc | 234 ++++++++++++++++++------------------------- gcc/cp/cp-gimplify.c | 7 +- gcc/cp/cp-tree.h | 7 +- gcc/cp/cvt.c | 2 +- 5 files changed, 110 insertions(+), 146 deletions(-) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index cd0a68e9fd6..7d96d577d84 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { /* Handle concept checks separately. */ if (concept_check_p (t)) - return evaluate_concept_check (t, tf_warning_or_error); + return evaluate_concept_check (t); location_t loc = cp_expr_loc_or_input_loc (t); tree fun = get_function_named_in_call (t); @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, '!requires (T t) { ... }' which is not transformed into a constraint. */ if (!processing_template_decl) - return satisfy_constraint_expression (t); + return evaluate_requires_expr (t); else *non_constant_p = true; return t; @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (!processing_template_decl && !uid_sensitive_constexpr_evaluation_p ()) - r = evaluate_concept_check (t, tf_warning_or_error); + r = evaluate_concept_check (t); else *non_constant_p = true; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 3332e6092a9..0949788aa29 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -107,10 +107,9 @@ struct subst_info a constraint is not satisfied. The entrypoints to satisfaction for which we set noisy+unsat are - diagnose_constraints and diagnose_nested_requirement. The entrypoints for - which we set noisy-unsat are the replays inside constraint_satisfaction_value, - evaluate_concept_check and tsubst_nested_requirement. In other entrypoints, - e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags + 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). */ struct sat_info : subst_info @@ -133,7 +132,7 @@ struct sat_info : subst_info bool diagnose_unsatisfaction; }; -static tree satisfy_constraint_expression (tree, tree, sat_info); +static tree constraint_satisfaction_value (tree, tree, sat_info); /* True if T is known to be some type other than bool. Note that this is false for dependent types and errors. */ @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false) return norm; } -/* Returns the normal form of TMPL's requirements. */ - -static tree -normalize_template_requirements (tree tmpl, bool diag = false) -{ - return get_normalized_constraints_from_decl (tmpl, diag); -} - -/* Returns the normal form of TMPL's requirements. */ - -static tree -normalize_nontemplate_requirements (tree decl, bool diag = false) -{ - return get_normalized_constraints_from_decl (decl, diag); -} - /* Normalize an EXPR as a constraint. */ static tree @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info) static tree tsubst_nested_requirement (tree t, tree args, subst_info info) { - /* Perform satisfaction quietly first. */ sat_info quiet (tf_none, info.in_decl); - tree result = satisfy_constraint_expression (t, args, quiet); - if (result == error_mark_node) - { - /* Replay the error. */ - sat_info noisy (tf_warning_or_error, info.in_decl); - satisfy_constraint_expression (t, args, noisy); - } + tree result = constraint_satisfaction_value (t, args, quiet); if (result != boolean_true_node) return error_mark_node; return boolean_true_node; @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry> /* Cache the result of satisfy_atom. */ static GTY((deletable)) hash_table<sat_hasher> *sat_cache; -/* Cache the result of constraint_satisfaction_value. */ +/* Cache the result of satisfy_declaration_constraints. */ static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; /* A tool used by satisfy_atom to help manage satisfaction caching and to @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info) /* Check that the normalized constraint T is satisfied for ARGS. */ static tree -satisfy_constraint (tree t, tree args, sat_info info) +satisfy_normalized_constraints (tree t, tree args, sat_info info) { auto_timevar time (TV_CONSTRAINT_SAT); @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info) return satisfy_constraint_r (t, args, info); } -/* Check the normalized constraints T against ARGS, returning a satisfaction - value (either true, false, or error). */ - -static tree -satisfy_associated_constraints (tree t, tree args, sat_info info) -{ - /* If there are no constraints then this is trivially satisfied. */ - if (!t) - return boolean_true_node; - - /* If any arguments depend on template parameters, we can't - check constraints. Pretend they're satisfied for now. */ - if (args && uses_template_parms (args)) - return boolean_true_node; - - return satisfy_constraint (t, args, info); -} - /* Return the normal form of the constraints on the placeholder 'auto' type T. */ @@ -3005,19 +2963,34 @@ normalize_placeholder_type_constraints (tree t, bool diag) return normalize_constraint_expression (constr, info); } -/* Evaluate EXPR as a constraint expression using ARGS, returning a - satisfaction value. */ +/* Evaluate the constraints of T using ARGS, returning a satisfaction value. + Here, T can be a concept-id, nested-requirement, placeholder 'auto', or + requires-expression. */ static tree -satisfy_constraint_expression (tree t, tree args, sat_info info) +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info) { if (t == error_mark_node) return error_mark_node; + /* 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); + } + /* Get the normalized constraints. */ tree norm; - if (args == NULL_TREE && concept_check_p (t)) + if (concept_check_p (t)) { + gcc_assert (!args); tree id = unpack_concept_check (t); args = TREE_OPERAND (id, 1); tree tmpl = get_concept_check_template (id); @@ -3032,11 +3005,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) ninfo.initial_parms = TREE_TYPE (t); norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo); } - else if (EXPR_P (t)) - { - norm_info ninfo (info.noisy () ? tf_norm : tf_none); - norm = normalize_constraint_expression (t, ninfo); - } else if (is_auto (t)) { norm = normalize_placeholder_type_constraints (t, info.noisy ()); @@ -3047,23 +3015,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) gcc_unreachable (); /* Perform satisfaction. */ - return satisfy_constraint (norm, args, info); + return satisfy_normalized_constraints (norm, args, info); } -/* Used only to evaluate requires-expressions during constant expression - evaluation. */ - -tree -satisfy_constraint_expression (tree expr) -{ - sat_info info (tf_none, NULL_TREE); - return satisfy_constraint_expression (expr, NULL_TREE, info); -} +/* Evaluate the associated constraints of the template specialization T + according to INFO, returning a satisfaction value. */ static tree satisfy_declaration_constraints (tree t, sat_info info) { - gcc_assert (DECL_P (t)); + gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL); const tree saved_t = t; /* For inherited constructors, consider the original declaration; @@ -3083,26 +3044,24 @@ satisfy_declaration_constraints (tree t, sat_info info) if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t)) return *result; - /* Get the normalized constraints. */ - tree norm = NULL_TREE; tree args = NULL_TREE; if (tree ti = DECL_TEMPLATE_INFO (t)) { - tree tmpl = TI_TEMPLATE (ti); - norm = normalize_template_requirements (tmpl, info.noisy ()); - /* The initial parameter mapping is the complete set of template arguments substituted into the declaration. */ args = TI_ARGS (ti); if (inh_ctor_targs) args = add_outermost_template_args (args, inh_ctor_targs); - } - else - { - /* These should be empty until we allow constraints on non-templates. */ - norm = normalize_nontemplate_requirements (t, info.noisy ()); + + /* If any arguments depend on template parameters, we can't + check constraints. Pretend they're satisfied for now. */ + if (uses_template_parms (args)) + return boolean_true_node; } + /* Get the normalized constraints. */ + tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); + unsigned ftc_count = vec_safe_length (failed_type_completions); tree result = boolean_true_node; @@ -3111,7 +3070,7 @@ satisfy_declaration_constraints (tree t, sat_info info) if (!push_tinst_level (t)) return result; push_access_scope (t); - result = satisfy_associated_constraints (norm, args, info); + result = satisfy_normalized_constraints (norm, args, info); pop_access_scope (t); pop_tinst_level (); } @@ -3134,6 +3093,10 @@ satisfy_declaration_constraints (tree t, sat_info info) return result; } +/* Evaluate the associated constraints of the template T using ARGS as the + innermost set of template arguments and according to INFO, returning a + satisfaction value. */ + static tree satisfy_declaration_constraints (tree t, tree args, sat_info info) { @@ -3144,14 +3107,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) args = add_outermost_template_args (t, args); + /* If any arguments depend on template parameters, we can't + check constraints. Pretend they're satisfied for now. */ + if (uses_template_parms (args)) + return boolean_true_node; + tree result = boolean_true_node; - if (tree norm = normalize_template_requirements (t, info.noisy ())) + if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) { if (!push_tinst_level (t, args)) return result; tree pattern = DECL_TEMPLATE_RESULT (t); push_access_scope (pattern); - result = satisfy_associated_constraints (norm, args, info); + result = satisfy_normalized_constraints (norm, args, info); pop_access_scope (pattern); pop_tinst_level (); } @@ -3159,62 +3127,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) return result; } +/* A wrapper around satisfy_declaration_constraints and + satisfy_nondeclaration_constraints which additionally replays + quiet ill-formed satisfaction noisily, so that ill-formed + satisfaction always gets diagnosed. */ + static tree -constraint_satisfaction_value (tree t, sat_info info) +constraint_satisfaction_value (tree t, tree args, sat_info info) { tree r; if (DECL_P (t)) - r = satisfy_declaration_constraints (t, info); + { + if (args) + r = satisfy_declaration_constraints (t, args, info); + else + r = satisfy_declaration_constraints (t, info); + } else - r = satisfy_constraint_expression (t, NULL_TREE, info); + r = satisfy_nondeclaration_constraints (t, args, info); if (r == error_mark_node && info.quiet () && !(DECL_P (t) && TREE_NO_WARNING (t))) { - /* Replay the error with re-normalized requirements. */ + /* Replay the error noisily. */ sat_info noisy (tf_warning_or_error, info.in_decl); - constraint_satisfaction_value (t, noisy); - if (DECL_P (t)) + constraint_satisfaction_value (t, args, noisy); + if (DECL_P (t) && !args) /* Avoid giving these errors again. */ TREE_NO_WARNING (t) = true; } return r; } -static tree -constraint_satisfaction_value (tree t, tree args, sat_info info) -{ - tree r; - if (DECL_P (t)) - r = satisfy_declaration_constraints (t, args, info); - else - r = satisfy_constraint_expression (t, args, info); - if (r == error_mark_node && info.quiet ()) - { - /* Replay the error with re-normalized requirements. */ - sat_info noisy (tf_warning_or_error, info.in_decl); - constraint_satisfaction_value (t, args, noisy); - } - return r; -} - -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false - otherwise, even in the case of errors. */ - -bool -constraints_satisfied_p (tree t) -{ - if (!flag_concepts) - return true; - - sat_info quiet (tf_none, NULL_TREE); - return constraint_satisfaction_value (t, quiet) == boolean_true_node; -} +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE + and false otherwise, even in the case of errors. -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE - and false otherwise, even in the case of errors. */ + Here, T can be: + - a template declaration + - a template specialization (in which case ARGS must be empty) + - a concept-id (in which case ARGS must be empty) + - a nested-requirement + - a placeholder 'auto' + - a requires-expression. */ bool -constraints_satisfied_p (tree t, tree args) +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */) { if (!flag_concepts) return true; @@ -3227,7 +3183,7 @@ constraints_satisfied_p (tree t, tree args) evaluation of template-ids as id-expressions. */ tree -evaluate_concept_check (tree check, tsubst_flags_t complain) +evaluate_concept_check (tree check) { if (check == error_mark_node) return error_mark_node; @@ -3236,14 +3192,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain) /* Check for satisfaction without diagnostics. */ sat_info quiet (tf_none, NULL_TREE); - tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); - if (result == error_mark_node && (complain & tf_error)) - { - /* Replay the error with re-normalized requirements. */ - sat_info noisy (tf_warning_or_error, NULL_TREE); - satisfy_constraint_expression (check, NULL_TREE, noisy); - } - return result; + return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); +} + +/* Evaluate the requires-expression T, returning either boolean_true_node + or boolean_false_node. This is used during gimplification and constexpr + evaluation. */ + +tree +evaluate_requires_expr (tree t) +{ + gcc_assert (TREE_CODE (t) == REQUIRES_EXPR); + sat_info quiet (tf_none, NULL_TREE); + return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet); } /*--------------------------------------------------------------------------- @@ -3709,7 +3670,7 @@ diagnose_nested_requirement (tree req, tree args) { /* Quietly check for satisfaction first. */ sat_info quiet (tf_none, NULL_TREE); - tree result = satisfy_constraint_expression (req, args, quiet); + tree result = satisfy_nondeclaration_constraints (req, args, quiet); if (result == boolean_true_node) return; @@ -3721,7 +3682,7 @@ diagnose_nested_requirement (tree req, tree args) inform (loc, "nested requirement %qE is not satisfied, because", expr); sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true); - satisfy_constraint_expression (req, args, noisy); + satisfy_nondeclaration_constraints (req, args, noisy); } else inform (loc, "nested requirement %qE is not satisfied", expr); @@ -3854,7 +3815,7 @@ diagnosing_failed_constraint::replay_errors_p () } /* Emit diagnostics detailing the failure ARGS to satisfy the constraints - of T. Here, T can be either a constraint or a declaration. */ + of T. Here, T and ARGS are as in constraints_satisfied_p. */ void diagnose_constraints (location_t loc, tree t, tree args) @@ -3866,8 +3827,13 @@ 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 (!args) - constraint_satisfaction_value (t, noisy); + 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); diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index abb8a6ef078..df89ff3815b 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) normal functions. */ if (concept_check_p (stmt)) { - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); + *stmt_p = evaluate_concept_check (stmt); * walk_subtrees = 0; break; } @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) case REQUIRES_EXPR: /* Emit the value of the requires-expression. */ - *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt), - boolean_type_node); + *stmt_p = evaluate_requires_expr (stmt); *walk_subtrees = 0; break; case TEMPLATE_ID_EXPR: gcc_assert (concept_check_p (stmt)); /* Emit the value of the concept check. */ - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); + *stmt_p = evaluate_concept_check (stmt); walk_subtrees = 0; break; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 544e99538a4..f06ac3c73bf 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8109,6 +8109,7 @@ extern tree finish_compound_requirement (location_t, tree, tree, bool); extern tree finish_nested_requirement (location_t, tree); extern void check_constrained_friend (tree, tree); extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree); +extern tree evaluate_requires_expr (tree); extern tree tsubst_constraint (tree, tree, tsubst_flags_t, tree); extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree); extern tree tsubst_parameter_mapping (tree, tree, tsubst_flags_t, tree); @@ -8123,10 +8124,8 @@ struct processing_constraint_expression_sentinel extern bool processing_constraint_expression_p (); extern tree unpack_concept_check (tree); -extern tree evaluate_concept_check (tree, tsubst_flags_t); -extern tree satisfy_constraint_expression (tree); -extern bool constraints_satisfied_p (tree); -extern bool constraints_satisfied_p (tree, tree); +extern tree evaluate_concept_check (tree); +extern bool constraints_satisfied_p (tree, tree = NULL_TREE); extern bool* lookup_subsumption_result (tree, tree); extern bool save_subsumption_result (tree, tree, bool); extern tree find_template_parameters (tree, tree); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index e809f0e4068..3f5467c8283 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) /* Explicitly evaluate void-converted concept checks since their satisfaction may produce ill-formed programs. */ if (concept_check_p (expr)) - expr = evaluate_concept_check (expr, tf_warning_or_error); + expr = evaluate_concept_check (expr); if (VOID_TYPE_P (TREE_TYPE (expr))) return expr;
On 3/2/21 11:25 AM, Patrick Palka wrote: > On Mon, 1 Mar 2021, Jason Merrill wrote: > >> On 2/28/21 12:58 PM, Patrick Palka wrote: >>> This patch mostly performs some straightforward refactoring: >>> >>> - Renamed satisfy_constraint to satisfy_normalized_constraints >>> - Renamed the three-parameter version of satisfy_constraint_expression >>> to satisfy_nondeclaration_constraints >>> - Removed normalize_(non)?template_requirements >>> - Removed satisfy_associated_constraints (and made its callers >>> check for dependent template args sooner, before normalization) >>> - Removed the tsubst_flags_t parameter of evaluate_concept_check >>> - Combined the two versions of constraint_satisfaction_value >>> - Combined the two versions of constraint_satisfied_p >>> >>> Additionally, this patch removes the handling of bare >>> constraint-expressions from satisfy_nondeclaration_constraints, and >>> hence constraints_satisfied_p and constraint_satisfaction_value now only >>> take things that carry their own template information needed for >>> normalization. In practice, this only means it's no longer possible to >>> evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this >>> patch adjusts the affected callers to instead use tsubst_requires_expr. >> >> It's probably better to have a different entry point than tsubst_requires_expr >> for callers that have nothing to do with templates. Whether that's a one-line >> wrapper for the call to tsubst_requires_expr ("evaluate_requires_expr"?) or >> calling tsubst_requires_expr from satisfy_nondeclaration_constraints, I don't >> have an opinion either way. >> >>> For convenience, the function diagnose_constraints continues to accept >>> REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr >>> directly. >> >> This might argue for the latter choice above. > > Ah, I didn't consider the option of continuing to handle REQUIRES_EXPRs > from satisfy_nondeclaration_constraints by evaluating them directly. > > Here's a patch that implements this. I opted to add a > evaluate_requires_expr wrapper as well, since > > evaluate_requires_expr (t) > > is much more readable than > > constant_boolean_node (constraints_satisfied_p (t), > boolean_type_node) > > (Note the added TODO in satisfy_nondeclaration_constraints, which'll be > fixed in a new version of the REQUIRES_EXPR evaluation/diagnostic > unification patch.) OK. > -- >8 -- > > Subject: [PATCH] c++: Clean up normalization / satisfaction routines > > This patch mostly performs some straightforward refactoring: > > - Renamed satisfy_constraint to satisfy_normalized_constraints > - Renamed the three-parameter version of satisfy_constraint_expression > to satisfy_nondeclaration_constraints > - Removed normalize_(non)?template_requirements > - Removed satisfy_associated_constraints (and made its callers > check for dependent template args sooner, before normalization) > - Removed the tsubst_flags_t parameter of evaluate_concept_check > - Combined the two versions of constraint_satisfaction_value > - Combined the two versions of constraint_satisfied_p > > Additionally, this patch removes the handling of general > constraint-expressions from satisfy_nondeclaration_constraints, and > hence constraints_satisfied_p and constraint_satisfaction_value now take > only things that carry their own template information needed for > normalization, and, as a special case, REQUIRES_EXPRs. But the latter > now get evaluated directly via tsubst_requires_expr rather than going > through satisfaction. > > (That we used to evaluate REQUIRES_EXPR via satisfaction might even be a > correctness issue: since we cache satisfaction in special ways that don't > apply to regular evaluation, going through satisfaction could in theory > cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't > have.) > > gcc/cp/ChangeLog: > > * constexpr.c (cxx_eval_call_expression): Adjust call to > evaluate_concept_check. > (cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use > evaluate_requires_expression instead of > satisfy_constraint_expression. > <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. > * constraint.cc (struct sat_info): Adjust comment about which > satisfaction entrypoints use noisy-unsat. > (normalize_template_requirements): Remove (and adjust callers > appropriately). > (normalize_nontemplate_requirements): Likewise. > (tsubst_nested_requirement): Use constraint_satisfaction_value > instead of satisfy_constraint_expression, which'll do the > noisy replaying of ill-formed quiet satisfaction for us. > (decl_satisfied_cache): Adjust comment. > (satisfy_constraint): Rename to ... > (satisfy_normalized_constraints): ... this. > (satisfy_associated_constraints): Remove (and make its > callers check for dependent arguments). > (satisfy_constraint_expression): Rename to ... > (satisfy_nondeclaration_constraints): ... this. Assert that > 'args' is empty when 't' is a concept-id. Removing handling > bare constraint-expressions, and handle REQUIRES_EXPRs > specially. Adjust comment accordingly. > (satisfy_declaration_constraints): Assert in the two-parameter > version that 't' is not a TEMPLATE_DECL. Adjust following > removal of normalize_(non)?template_requirements and > satisfy_asociated_constraints. > (constraint_satisfaction_value): Combine the two- and > three-parameter versions in the natural way. > (constraints_satisfied_p): Combine the one- and two-parameter > versions in the natural way. Improve documentation. > (evaluate_requires_expr): Define. > (evaluate_concept_check): Remove 'complain' parameter. Use > constraint_satisfaction_value instead of > satisfy_constraint_expression. > (diagnose_nested_requirement): Adjust following renaming of > satisfy_constraint_expression. > (diagnose_constraints): Handle REQUIRES_EXPR by going through > diagnose_requires_expr directly instead of treating it as a > constraint-expression. Improve documentation. > * cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call > to evaluate_concept_check. > <case REQUIRES_EXPR>: Use evaluate_requires_expr instead of > constraints_satisfied_p. > <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check. > * cp-tree.h (evaluate_requires_expr): Declare. > (evaluate_concept_check): Remove tsubst_flag_t parameter. > (satisfy_constraint_expression): Remove declaration. > (constraints_satisfied_p): Remove one-parameter declaration. > Add a default argument to the two-parameter declaration. > * cvt.c (convert_to_void): Adjust call to > evaluate_concept_check. > --- > gcc/cp/constexpr.c | 6 +- > gcc/cp/constraint.cc | 234 ++++++++++++++++++------------------------- > gcc/cp/cp-gimplify.c | 7 +- > gcc/cp/cp-tree.h | 7 +- > gcc/cp/cvt.c | 2 +- > 5 files changed, 110 insertions(+), 146 deletions(-) > > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c > index cd0a68e9fd6..7d96d577d84 100644 > --- a/gcc/cp/constexpr.c > +++ b/gcc/cp/constexpr.c > @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > { > /* Handle concept checks separately. */ > if (concept_check_p (t)) > - return evaluate_concept_check (t, tf_warning_or_error); > + return evaluate_concept_check (t); > > location_t loc = cp_expr_loc_or_input_loc (t); > tree fun = get_function_named_in_call (t); > @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, > '!requires (T t) { ... }' which is not transformed into > a constraint. */ > if (!processing_template_decl) > - return satisfy_constraint_expression (t); > + return evaluate_requires_expr (t); > else > *non_constant_p = true; > return t; > @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, > > if (!processing_template_decl > && !uid_sensitive_constexpr_evaluation_p ()) > - r = evaluate_concept_check (t, tf_warning_or_error); > + r = evaluate_concept_check (t); > else > *non_constant_p = true; > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 3332e6092a9..0949788aa29 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -107,10 +107,9 @@ struct subst_info > a constraint is not satisfied. > > The entrypoints to satisfaction for which we set noisy+unsat are > - diagnose_constraints and diagnose_nested_requirement. The entrypoints for > - which we set noisy-unsat are the replays inside constraint_satisfaction_value, > - evaluate_concept_check and tsubst_nested_requirement. In other entrypoints, > - e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags > + 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). */ > > struct sat_info : subst_info > @@ -133,7 +132,7 @@ struct sat_info : subst_info > bool diagnose_unsatisfaction; > }; > > -static tree satisfy_constraint_expression (tree, tree, sat_info); > +static tree constraint_satisfaction_value (tree, tree, sat_info); > > /* True if T is known to be some type other than bool. Note that this > is false for dependent types and errors. */ > @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false) > return norm; > } > > -/* Returns the normal form of TMPL's requirements. */ > - > -static tree > -normalize_template_requirements (tree tmpl, bool diag = false) > -{ > - return get_normalized_constraints_from_decl (tmpl, diag); > -} > - > -/* Returns the normal form of TMPL's requirements. */ > - > -static tree > -normalize_nontemplate_requirements (tree decl, bool diag = false) > -{ > - return get_normalized_constraints_from_decl (decl, diag); > -} > - > /* Normalize an EXPR as a constraint. */ > > static tree > @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info) > static tree > tsubst_nested_requirement (tree t, tree args, subst_info info) > { > - /* Perform satisfaction quietly first. */ > sat_info quiet (tf_none, info.in_decl); > - tree result = satisfy_constraint_expression (t, args, quiet); > - if (result == error_mark_node) > - { > - /* Replay the error. */ > - sat_info noisy (tf_warning_or_error, info.in_decl); > - satisfy_constraint_expression (t, args, noisy); > - } > + tree result = constraint_satisfaction_value (t, args, quiet); > if (result != boolean_true_node) > return error_mark_node; > return boolean_true_node; > @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry> > /* Cache the result of satisfy_atom. */ > static GTY((deletable)) hash_table<sat_hasher> *sat_cache; > > -/* Cache the result of constraint_satisfaction_value. */ > +/* Cache the result of satisfy_declaration_constraints. */ > static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; > > /* A tool used by satisfy_atom to help manage satisfaction caching and to > @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info) > /* Check that the normalized constraint T is satisfied for ARGS. */ > > static tree > -satisfy_constraint (tree t, tree args, sat_info info) > +satisfy_normalized_constraints (tree t, tree args, sat_info info) > { > auto_timevar time (TV_CONSTRAINT_SAT); > > @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info) > return satisfy_constraint_r (t, args, info); > } > > -/* Check the normalized constraints T against ARGS, returning a satisfaction > - value (either true, false, or error). */ > - > -static tree > -satisfy_associated_constraints (tree t, tree args, sat_info info) > -{ > - /* If there are no constraints then this is trivially satisfied. */ > - if (!t) > - return boolean_true_node; > - > - /* If any arguments depend on template parameters, we can't > - check constraints. Pretend they're satisfied for now. */ > - if (args && uses_template_parms (args)) > - return boolean_true_node; > - > - return satisfy_constraint (t, args, info); > -} > - > /* Return the normal form of the constraints on the placeholder 'auto' > type T. */ > > @@ -3005,19 +2963,34 @@ normalize_placeholder_type_constraints (tree t, bool diag) > return normalize_constraint_expression (constr, info); > } > > -/* Evaluate EXPR as a constraint expression using ARGS, returning a > - satisfaction value. */ > +/* Evaluate the constraints of T using ARGS, returning a satisfaction value. > + Here, T can be a concept-id, nested-requirement, placeholder 'auto', or > + requires-expression. */ > > static tree > -satisfy_constraint_expression (tree t, tree args, sat_info info) > +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info) > { > if (t == error_mark_node) > return error_mark_node; > > + /* 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); > + } > + > /* Get the normalized constraints. */ > tree norm; > - if (args == NULL_TREE && concept_check_p (t)) > + if (concept_check_p (t)) > { > + gcc_assert (!args); > tree id = unpack_concept_check (t); > args = TREE_OPERAND (id, 1); > tree tmpl = get_concept_check_template (id); > @@ -3032,11 +3005,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) > ninfo.initial_parms = TREE_TYPE (t); > norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo); > } > - else if (EXPR_P (t)) > - { > - norm_info ninfo (info.noisy () ? tf_norm : tf_none); > - norm = normalize_constraint_expression (t, ninfo); > - } > else if (is_auto (t)) > { > norm = normalize_placeholder_type_constraints (t, info.noisy ()); > @@ -3047,23 +3015,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) > gcc_unreachable (); > > /* Perform satisfaction. */ > - return satisfy_constraint (norm, args, info); > + return satisfy_normalized_constraints (norm, args, info); > } > > -/* Used only to evaluate requires-expressions during constant expression > - evaluation. */ > - > -tree > -satisfy_constraint_expression (tree expr) > -{ > - sat_info info (tf_none, NULL_TREE); > - return satisfy_constraint_expression (expr, NULL_TREE, info); > -} > +/* Evaluate the associated constraints of the template specialization T > + according to INFO, returning a satisfaction value. */ > > static tree > satisfy_declaration_constraints (tree t, sat_info info) > { > - gcc_assert (DECL_P (t)); > + gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL); > const tree saved_t = t; > > /* For inherited constructors, consider the original declaration; > @@ -3083,26 +3044,24 @@ satisfy_declaration_constraints (tree t, sat_info info) > if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t)) > return *result; > > - /* Get the normalized constraints. */ > - tree norm = NULL_TREE; > tree args = NULL_TREE; > if (tree ti = DECL_TEMPLATE_INFO (t)) > { > - tree tmpl = TI_TEMPLATE (ti); > - norm = normalize_template_requirements (tmpl, info.noisy ()); > - > /* The initial parameter mapping is the complete set of > template arguments substituted into the declaration. */ > args = TI_ARGS (ti); > if (inh_ctor_targs) > args = add_outermost_template_args (args, inh_ctor_targs); > - } > - else > - { > - /* These should be empty until we allow constraints on non-templates. */ > - norm = normalize_nontemplate_requirements (t, info.noisy ()); > + > + /* If any arguments depend on template parameters, we can't > + check constraints. Pretend they're satisfied for now. */ > + if (uses_template_parms (args)) > + return boolean_true_node; > } > > + /* Get the normalized constraints. */ > + tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); > + > unsigned ftc_count = vec_safe_length (failed_type_completions); > > tree result = boolean_true_node; > @@ -3111,7 +3070,7 @@ satisfy_declaration_constraints (tree t, sat_info info) > if (!push_tinst_level (t)) > return result; > push_access_scope (t); > - result = satisfy_associated_constraints (norm, args, info); > + result = satisfy_normalized_constraints (norm, args, info); > pop_access_scope (t); > pop_tinst_level (); > } > @@ -3134,6 +3093,10 @@ satisfy_declaration_constraints (tree t, sat_info info) > return result; > } > > +/* Evaluate the associated constraints of the template T using ARGS as the > + innermost set of template arguments and according to INFO, returning a > + satisfaction value. */ > + > static tree > satisfy_declaration_constraints (tree t, tree args, sat_info info) > { > @@ -3144,14 +3107,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > > args = add_outermost_template_args (t, args); > > + /* If any arguments depend on template parameters, we can't > + check constraints. Pretend they're satisfied for now. */ > + if (uses_template_parms (args)) > + return boolean_true_node; > + > tree result = boolean_true_node; > - if (tree norm = normalize_template_requirements (t, info.noisy ())) > + if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) > { > if (!push_tinst_level (t, args)) > return result; > tree pattern = DECL_TEMPLATE_RESULT (t); > push_access_scope (pattern); > - result = satisfy_associated_constraints (norm, args, info); > + result = satisfy_normalized_constraints (norm, args, info); > pop_access_scope (pattern); > pop_tinst_level (); > } > @@ -3159,62 +3127,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > return result; > } > > +/* A wrapper around satisfy_declaration_constraints and > + satisfy_nondeclaration_constraints which additionally replays > + quiet ill-formed satisfaction noisily, so that ill-formed > + satisfaction always gets diagnosed. */ > + > static tree > -constraint_satisfaction_value (tree t, sat_info info) > +constraint_satisfaction_value (tree t, tree args, sat_info info) > { > tree r; > if (DECL_P (t)) > - r = satisfy_declaration_constraints (t, info); > + { > + if (args) > + r = satisfy_declaration_constraints (t, args, info); > + else > + r = satisfy_declaration_constraints (t, info); > + } > else > - r = satisfy_constraint_expression (t, NULL_TREE, info); > + r = satisfy_nondeclaration_constraints (t, args, info); > if (r == error_mark_node && info.quiet () > && !(DECL_P (t) && TREE_NO_WARNING (t))) > { > - /* Replay the error with re-normalized requirements. */ > + /* Replay the error noisily. */ > sat_info noisy (tf_warning_or_error, info.in_decl); > - constraint_satisfaction_value (t, noisy); > - if (DECL_P (t)) > + constraint_satisfaction_value (t, args, noisy); > + if (DECL_P (t) && !args) > /* Avoid giving these errors again. */ > TREE_NO_WARNING (t) = true; > } > return r; > } > > -static tree > -constraint_satisfaction_value (tree t, tree args, sat_info info) > -{ > - tree r; > - if (DECL_P (t)) > - r = satisfy_declaration_constraints (t, args, info); > - else > - r = satisfy_constraint_expression (t, args, info); > - if (r == error_mark_node && info.quiet ()) > - { > - /* Replay the error with re-normalized requirements. */ > - sat_info noisy (tf_warning_or_error, info.in_decl); > - constraint_satisfaction_value (t, args, noisy); > - } > - return r; > -} > - > -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false > - otherwise, even in the case of errors. */ > - > -bool > -constraints_satisfied_p (tree t) > -{ > - if (!flag_concepts) > - return true; > - > - sat_info quiet (tf_none, NULL_TREE); > - return constraint_satisfaction_value (t, quiet) == boolean_true_node; > -} > +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE > + and false otherwise, even in the case of errors. > > -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE > - and false otherwise, even in the case of errors. */ > + Here, T can be: > + - a template declaration > + - a template specialization (in which case ARGS must be empty) > + - a concept-id (in which case ARGS must be empty) > + - a nested-requirement > + - a placeholder 'auto' > + - a requires-expression. */ > > bool > -constraints_satisfied_p (tree t, tree args) > +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */) > { > if (!flag_concepts) > return true; > @@ -3227,7 +3183,7 @@ constraints_satisfied_p (tree t, tree args) > evaluation of template-ids as id-expressions. */ > > tree > -evaluate_concept_check (tree check, tsubst_flags_t complain) > +evaluate_concept_check (tree check) > { > if (check == error_mark_node) > return error_mark_node; > @@ -3236,14 +3192,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain) > > /* Check for satisfaction without diagnostics. */ > sat_info quiet (tf_none, NULL_TREE); > - tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); > - if (result == error_mark_node && (complain & tf_error)) > - { > - /* Replay the error with re-normalized requirements. */ > - sat_info noisy (tf_warning_or_error, NULL_TREE); > - satisfy_constraint_expression (check, NULL_TREE, noisy); > - } > - return result; > + return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); > +} > + > +/* Evaluate the requires-expression T, returning either boolean_true_node > + or boolean_false_node. This is used during gimplification and constexpr > + evaluation. */ > + > +tree > +evaluate_requires_expr (tree t) > +{ > + gcc_assert (TREE_CODE (t) == REQUIRES_EXPR); > + sat_info quiet (tf_none, NULL_TREE); > + return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet); > } > > /*--------------------------------------------------------------------------- > @@ -3709,7 +3670,7 @@ diagnose_nested_requirement (tree req, tree args) > { > /* Quietly check for satisfaction first. */ > sat_info quiet (tf_none, NULL_TREE); > - tree result = satisfy_constraint_expression (req, args, quiet); > + tree result = satisfy_nondeclaration_constraints (req, args, quiet); > if (result == boolean_true_node) > return; > > @@ -3721,7 +3682,7 @@ diagnose_nested_requirement (tree req, tree args) > inform (loc, "nested requirement %qE is not satisfied, because", expr); > > sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true); > - satisfy_constraint_expression (req, args, noisy); > + satisfy_nondeclaration_constraints (req, args, noisy); > } > else > inform (loc, "nested requirement %qE is not satisfied", expr); > @@ -3854,7 +3815,7 @@ diagnosing_failed_constraint::replay_errors_p () > } > > /* Emit diagnostics detailing the failure ARGS to satisfy the constraints > - of T. Here, T can be either a constraint or a declaration. */ > + of T. Here, T and ARGS are as in constraints_satisfied_p. */ > > void > diagnose_constraints (location_t loc, tree t, tree args) > @@ -3866,8 +3827,13 @@ 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 (!args) > - constraint_satisfaction_value (t, noisy); > + 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); > > diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c > index abb8a6ef078..df89ff3815b 100644 > --- a/gcc/cp/cp-gimplify.c > +++ b/gcc/cp/cp-gimplify.c > @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) > normal functions. */ > if (concept_check_p (stmt)) > { > - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); > + *stmt_p = evaluate_concept_check (stmt); > * walk_subtrees = 0; > break; > } > @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) > > case REQUIRES_EXPR: > /* Emit the value of the requires-expression. */ > - *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt), > - boolean_type_node); > + *stmt_p = evaluate_requires_expr (stmt); > *walk_subtrees = 0; > break; > > case TEMPLATE_ID_EXPR: > gcc_assert (concept_check_p (stmt)); > /* Emit the value of the concept check. */ > - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); > + *stmt_p = evaluate_concept_check (stmt); > walk_subtrees = 0; > break; > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 544e99538a4..f06ac3c73bf 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -8109,6 +8109,7 @@ extern tree finish_compound_requirement (location_t, tree, tree, bool); > extern tree finish_nested_requirement (location_t, tree); > extern void check_constrained_friend (tree, tree); > extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree); > +extern tree evaluate_requires_expr (tree); > extern tree tsubst_constraint (tree, tree, tsubst_flags_t, tree); > extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree); > extern tree tsubst_parameter_mapping (tree, tree, tsubst_flags_t, tree); > @@ -8123,10 +8124,8 @@ struct processing_constraint_expression_sentinel > extern bool processing_constraint_expression_p (); > > extern tree unpack_concept_check (tree); > -extern tree evaluate_concept_check (tree, tsubst_flags_t); > -extern tree satisfy_constraint_expression (tree); > -extern bool constraints_satisfied_p (tree); > -extern bool constraints_satisfied_p (tree, tree); > +extern tree evaluate_concept_check (tree); > +extern bool constraints_satisfied_p (tree, tree = NULL_TREE); > extern bool* lookup_subsumption_result (tree, tree); > extern bool save_subsumption_result (tree, tree, bool); > extern tree find_template_parameters (tree, tree); > diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c > index e809f0e4068..3f5467c8283 100644 > --- a/gcc/cp/cvt.c > +++ b/gcc/cp/cvt.c > @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) > /* Explicitly evaluate void-converted concept checks since their > satisfaction may produce ill-formed programs. */ > if (concept_check_p (expr)) > - expr = evaluate_concept_check (expr, tf_warning_or_error); > + expr = evaluate_concept_check (expr); > > if (VOID_TYPE_P (TREE_TYPE (expr))) > return expr; >
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index cd0a68e9fd6..f940e3e5985 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { /* Handle concept checks separately. */ if (concept_check_p (t)) - return evaluate_concept_check (t, tf_warning_or_error); + return evaluate_concept_check (t); location_t loc = cp_expr_loc_or_input_loc (t); tree fun = get_function_named_in_call (t); @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, '!requires (T t) { ... }' which is not transformed into a constraint. */ if (!processing_template_decl) - return satisfy_constraint_expression (t); + return tsubst_requires_expr (t, NULL_TREE, tf_none, NULL_TREE); else *non_constant_p = true; return t; @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (!processing_template_decl && !uid_sensitive_constexpr_evaluation_p ()) - r = evaluate_concept_check (t, tf_warning_or_error); + r = evaluate_concept_check (t); else *non_constant_p = true; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 2b61ad8d9ea..cf319b34da0 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -107,10 +107,9 @@ struct subst_info a constraint is not satisfied. The entrypoints to satisfaction for which we set noisy+unsat are - diagnose_constraints and diagnose_nested_requirement. The entrypoints for - which we set noisy-unsat are the replays inside constraint_satisfaction_value, - evaluate_concept_check and tsubst_nested_requirement. In other entrypoints, - e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags + 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). */ struct sat_info : subst_info @@ -133,7 +132,7 @@ struct sat_info : subst_info bool diagnose_unsatisfaction; }; -static tree satisfy_constraint_expression (tree, tree, sat_info); +static tree constraint_satisfaction_value (tree, tree, sat_info); /* True if T is known to be some type other than bool. Note that this is false for dependent types and errors. */ @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false) return norm; } -/* Returns the normal form of TMPL's requirements. */ - -static tree -normalize_template_requirements (tree tmpl, bool diag = false) -{ - return get_normalized_constraints_from_decl (tmpl, diag); -} - -/* Returns the normal form of TMPL's requirements. */ - -static tree -normalize_nontemplate_requirements (tree decl, bool diag = false) -{ - return get_normalized_constraints_from_decl (decl, diag); -} - /* Normalize an EXPR as a constraint. */ static tree @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info) static tree tsubst_nested_requirement (tree t, tree args, subst_info info) { - /* Perform satisfaction quietly first. */ sat_info quiet (tf_none, info.in_decl); - tree result = satisfy_constraint_expression (t, args, quiet); - if (result == error_mark_node) - { - /* Replay the error. */ - sat_info noisy (tf_warning_or_error, info.in_decl); - satisfy_constraint_expression (t, args, noisy); - } + tree result = constraint_satisfaction_value (t, args, quiet); if (result != boolean_true_node) return error_mark_node; return boolean_true_node; @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry> /* Cache the result of satisfy_atom. */ static GTY((deletable)) hash_table<sat_hasher> *sat_cache; -/* Cache the result of constraint_satisfaction_value. */ +/* Cache the result of satisfy_declaration_constraints. */ static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; /* A tool used by satisfy_atom to help manage satisfaction caching and to @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info) /* Check that the normalized constraint T is satisfied for ARGS. */ static tree -satisfy_constraint (tree t, tree args, sat_info info) +satisfy_normalized_constraints (tree t, tree args, sat_info info) { auto_timevar time (TV_CONSTRAINT_SAT); @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info) return satisfy_constraint_r (t, args, info); } -/* Check the normalized constraints T against ARGS, returning a satisfaction - value (either true, false, or error). */ - -static tree -satisfy_associated_constraints (tree t, tree args, sat_info info) -{ - /* If there are no constraints then this is trivially satisfied. */ - if (!t) - return boolean_true_node; - - /* If any arguments depend on template parameters, we can't - check constraints. Pretend they're satisfied for now. */ - if (args && uses_template_parms (args)) - return boolean_true_node; - - return satisfy_constraint (t, args, info); -} - /* Return the normal form of the constraints on the placeholder 'auto' type T. */ @@ -3005,19 +2963,20 @@ normalize_placeholder_type_constraints (tree t, bool diag) return normalize_constraint_expression (constr, info); } -/* Evaluate EXPR as a constraint expression using ARGS, returning a - satisfaction value. */ +/* Evaluate the constraints of T using ARGS, returning a satisfaction value. + Here, T can be a concept-id, nested-requirement or placeholder 'auto'. */ static tree -satisfy_constraint_expression (tree t, tree args, sat_info info) +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info) { if (t == error_mark_node) return error_mark_node; /* Get the normalized constraints. */ tree norm; - if (args == NULL_TREE && concept_check_p (t)) + if (concept_check_p (t)) { + gcc_assert (!args); tree id = unpack_concept_check (t); args = TREE_OPERAND (id, 1); tree tmpl = get_concept_check_template (id); @@ -3032,11 +2991,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) ninfo.initial_parms = TREE_TYPE (t); norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo); } - else if (EXPR_P (t)) - { - norm_info ninfo (info.noisy () ? tf_norm : tf_none); - norm = normalize_constraint_expression (t, ninfo); - } else if (is_auto (t)) { norm = normalize_placeholder_type_constraints (t, info.noisy ()); @@ -3047,23 +3001,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) gcc_unreachable (); /* Perform satisfaction. */ - return satisfy_constraint (norm, args, info); + return satisfy_normalized_constraints (norm, args, info); } -/* Used only to evaluate requires-expressions during constant expression - evaluation. */ - -tree -satisfy_constraint_expression (tree expr) -{ - sat_info info (tf_none, NULL_TREE); - return satisfy_constraint_expression (expr, NULL_TREE, info); -} +/* Evaluate the associated constraints of the template specialization T + according to INFO, returning a satisfaction value. */ static tree satisfy_declaration_constraints (tree t, sat_info info) { - gcc_assert (DECL_P (t)); + gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL); const tree saved_t = t; /* For inherited constructors, consider the original declaration; @@ -3083,26 +3030,24 @@ satisfy_declaration_constraints (tree t, sat_info info) if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t)) return *result; - /* Get the normalized constraints. */ - tree norm = NULL_TREE; tree args = NULL_TREE; if (tree ti = DECL_TEMPLATE_INFO (t)) { - tree tmpl = TI_TEMPLATE (ti); - norm = normalize_template_requirements (tmpl, info.noisy ()); - /* The initial parameter mapping is the complete set of template arguments substituted into the declaration. */ args = TI_ARGS (ti); if (inh_ctor_targs) args = add_outermost_template_args (args, inh_ctor_targs); - } - else - { - /* These should be empty until we allow constraints on non-templates. */ - norm = normalize_nontemplate_requirements (t, info.noisy ()); + + /* If any arguments depend on template parameters, we can't + check constraints. Pretend they're satisfied for now. */ + if (uses_template_parms (args)) + return boolean_true_node; } + /* Get the normalized constraints. */ + tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); + unsigned ftc_count = vec_safe_length (failed_type_completions); tree result = boolean_true_node; @@ -3111,7 +3056,7 @@ satisfy_declaration_constraints (tree t, sat_info info) if (!push_tinst_level (t)) return result; push_access_scope (t); - result = satisfy_associated_constraints (norm, args, info); + result = satisfy_normalized_constraints (norm, args, info); pop_access_scope (t); pop_tinst_level (); } @@ -3134,6 +3079,10 @@ satisfy_declaration_constraints (tree t, sat_info info) return result; } +/* Evaluate the associated constraints of the template T using ARGS as the + innermost set of template arguments and according to INFO, returning a + satisfaction value. */ + static tree satisfy_declaration_constraints (tree t, tree args, sat_info info) { @@ -3144,14 +3093,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) args = add_outermost_template_args (t, args); + /* If any arguments depend on template parameters, we can't + check constraints. Pretend they're satisfied for now. */ + if (uses_template_parms (args)) + return boolean_true_node; + tree result = boolean_true_node; - if (tree norm = normalize_template_requirements (t, info.noisy ())) + if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) { if (!push_tinst_level (t, args)) return result; tree pattern = DECL_TEMPLATE_RESULT (t); push_access_scope (pattern); - result = satisfy_associated_constraints (norm, args, info); + result = satisfy_normalized_constraints (norm, args, info); pop_access_scope (pattern); pop_tinst_level (); } @@ -3159,62 +3113,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) return result; } +/* A wrapper around satisfy_declaration_constraints and + satisfy_nondeclaration_constraints which additionally replays + quiet ill-formed satisfaction noisily, so that ill-formed + satisfaction always gets diagnosed. */ + static tree -constraint_satisfaction_value (tree t, sat_info info) +constraint_satisfaction_value (tree t, tree args, sat_info info) { tree r; if (DECL_P (t)) - r = satisfy_declaration_constraints (t, info); + { + if (args) + r = satisfy_declaration_constraints (t, args, info); + else + r = satisfy_declaration_constraints (t, info); + } else - r = satisfy_constraint_expression (t, NULL_TREE, info); + r = satisfy_nondeclaration_constraints (t, args, info); if (r == error_mark_node && info.quiet () && !(DECL_P (t) && TREE_NO_WARNING (t))) { - /* Replay the error with re-normalized requirements. */ + /* Replay the error noisily. */ sat_info noisy (tf_warning_or_error, info.in_decl); - constraint_satisfaction_value (t, noisy); - if (DECL_P (t)) + constraint_satisfaction_value (t, args, noisy); + if (DECL_P (t) && !args) /* Avoid giving these errors again. */ TREE_NO_WARNING (t) = true; } return r; } -static tree -constraint_satisfaction_value (tree t, tree args, sat_info info) -{ - tree r; - if (DECL_P (t)) - r = satisfy_declaration_constraints (t, args, info); - else - r = satisfy_constraint_expression (t, args, info); - if (r == error_mark_node && info.quiet ()) - { - /* Replay the error with re-normalized requirements. */ - sat_info noisy (tf_warning_or_error, info.in_decl); - constraint_satisfaction_value (t, args, noisy); - } - return r; -} - -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false - otherwise, even in the case of errors. */ +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE + and false otherwise, even in the case of errors. -bool -constraints_satisfied_p (tree t) -{ - if (!flag_concepts) - return true; - - sat_info quiet (tf_none, NULL_TREE); - return constraint_satisfaction_value (t, quiet) == boolean_true_node; -} - -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE - and false otherwise, even in the case of errors. */ + Here, T can be: + - a template declaration (in which case ARGS is an innermost template + argument set for T) + - a template specialization (in which case ARGS must be empty) + - a concept-id (in which case ARGS must be empty) + - a nested-requirement (in which case ARGS is a complete argument set) + - a placeholder 'auto' (in which case ARGS is a complete argument set). */ bool -constraints_satisfied_p (tree t, tree args) +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */) { if (!flag_concepts) return true; @@ -3227,7 +3169,7 @@ constraints_satisfied_p (tree t, tree args) evaluation of template-ids as id-expressions. */ tree -evaluate_concept_check (tree check, tsubst_flags_t complain) +evaluate_concept_check (tree check) { if (check == error_mark_node) return error_mark_node; @@ -3236,14 +3178,7 @@ evaluate_concept_check (tree check, tsubst_flags_t complain) /* Check for satisfaction without diagnostics. */ sat_info quiet (tf_none, NULL_TREE); - tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); - if (result == error_mark_node && (complain & tf_error)) - { - /* Replay the error with re-normalized requirements. */ - sat_info noisy (tf_warning_or_error, NULL_TREE); - satisfy_constraint_expression (check, NULL_TREE, noisy); - } - return result; + return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); } /*--------------------------------------------------------------------------- @@ -3709,7 +3644,7 @@ diagnose_nested_requirement (tree req, tree args) { /* Quietly check for satisfaction first. */ sat_info quiet (tf_none, NULL_TREE); - tree result = satisfy_constraint_expression (req, args, quiet); + tree result = satisfy_nondeclaration_constraints (req, args, quiet); if (result == boolean_true_node) return; @@ -3721,7 +3656,7 @@ diagnose_nested_requirement (tree req, tree args) inform (loc, "nested requirement %qE is not satisfied, because", expr); sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true); - satisfy_constraint_expression (req, args, noisy); + satisfy_nondeclaration_constraints (req, args, noisy); } else inform (loc, "nested requirement %qE is not satisfied", expr); @@ -3854,7 +3789,9 @@ diagnosing_failed_constraint::replay_errors_p () } /* Emit diagnostics detailing the failure ARGS to satisfy the constraints - of T. Here, T can be either a constraint or a declaration. */ + of T. Here, T and ARGS are as in constraints_satisfied_p, except that T + can also be a REQUIRES_EXPR (in which case ARGS is must be empty). The + latter is used by finish_static_assert to diagnose a false REQUIRES_EXPR. */ void diagnose_constraints (location_t loc, tree t, tree args) @@ -3866,8 +3803,13 @@ 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 (!args) - constraint_satisfaction_value (t, noisy); + 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); diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index abb8a6ef078..64b1dc1b433 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) normal functions. */ if (concept_check_p (stmt)) { - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); + *stmt_p = evaluate_concept_check (stmt); * walk_subtrees = 0; break; } @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) case REQUIRES_EXPR: /* Emit the value of the requires-expression. */ - *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt), - boolean_type_node); + *stmt_p = tsubst_requires_expr (stmt, NULL_TREE, tf_none, NULL_TREE); *walk_subtrees = 0; break; case TEMPLATE_ID_EXPR: gcc_assert (concept_check_p (stmt)); /* Emit the value of the concept check. */ - *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error); + *stmt_p = evaluate_concept_check (stmt); walk_subtrees = 0; break; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 544e99538a4..995e13f4a6e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8123,10 +8123,8 @@ struct processing_constraint_expression_sentinel extern bool processing_constraint_expression_p (); extern tree unpack_concept_check (tree); -extern tree evaluate_concept_check (tree, tsubst_flags_t); -extern tree satisfy_constraint_expression (tree); -extern bool constraints_satisfied_p (tree); -extern bool constraints_satisfied_p (tree, tree); +extern tree evaluate_concept_check (tree); +extern bool constraints_satisfied_p (tree, tree = NULL_TREE); extern bool* lookup_subsumption_result (tree, tree); extern bool save_subsumption_result (tree, tree, bool); extern tree find_template_parameters (tree, tree); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index e809f0e4068..3f5467c8283 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) /* Explicitly evaluate void-converted concept checks since their satisfaction may produce ill-formed programs. */ if (concept_check_p (expr)) - expr = evaluate_concept_check (expr, tf_warning_or_error); + expr = evaluate_concept_check (expr); if (VOID_TYPE_P (TREE_TYPE (expr))) return expr;