From 1906392b2c9a02da82d1739386219ec0f13bcc33 Mon Sep 17 00:00:00 2001 From: Andrew Sutton Date: Wed, 27 Nov 2019 14:02:49 +0000 Subject: [PATCH] Diagnose certain constraint errors as hard errors, but otherwise treat them the same as normal SFINAE-type errors. 2019-11-27 Andrew Sutton Diagnose certain constraint errors as hard errors, but otherwise treat them the same as normal SFINAE-type errors. Also, generally clean up the satisfaction functions. gcc/cp/ * constexpr.c (cxx_eval_constant_expression): Use evaluate_concept_check. * constraint.cc (normalize_concept_definition): Accept a diagnostic flag and only cache when not diagnosing errors. (decl_satisfied_cache): Map to trees instead of bools. (satisfy_atom): Guarantee a location for the errors, propagate complain flags to force_rvalue, and emit errors for non-boolean constraints. (get_normalized_constraints_and_args): New overloads. Factored out of satisfy_constraint_expression and satisfy_declaration_constraints. (satisfy_constraint_expression): Propagate diagnostic info to normalization. (satisfy_declaration_constraints): New. Factored out of constraints_satisfied_p. (constraint_satisfaction_value): New. Calls satisfy_constraint_expression or satisfy_declaration_constraints. (constraints_satisfied_p): Call constraint_satisfaction_value. (evaluate_concept_check): Don't take tsubst_falgs_t. Replay satisfaction if an error is encountered. (current_failed_constraint): Moved from pt.c. (diagnose_constraints): Call constraint_satisfaction_value. * cp-tree.h: Update declarations. * pt.c (current_failed_constraint): Moved to constraint.cc. * semantics.c (finish_id_expression_1): Remove a duplicate case. gcc/testsuite/ * g++.dg/concepts/pr84330.C: Update diagnostics. * g++.dg/cpp2a/concepts-requires2.C: Likewise. From-SVN: r278768 --- gcc/cp/ChangeLog | 30 +++ gcc/cp/constexpr.c | 2 +- gcc/cp/constraint.cc | 190 +++++++++--------- gcc/cp/cp-tree.h | 4 +- gcc/cp/error.c | 3 + gcc/cp/pt.c | 2 - gcc/cp/semantics.c | 10 - gcc/testsuite/ChangeLog | 6 + gcc/testsuite/g++.dg/concepts/pr84330.C | 2 +- .../g++.dg/cpp2a/concepts-requires2.C | 12 +- 10 files changed, 145 insertions(+), 116 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4cbdb4352dc..5f770e939d8 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,33 @@ +2019-11-27 Andrew Sutton + + Diagnose certain constraint errors as hard errors, but otherwise treat + them the same as normal SFINAE-type errors. Also, generally clean up + the satisfaction functions. + + * constexpr.c (cxx_eval_constant_expression): Use + evaluate_concept_check. + * constraint.cc (normalize_concept_definition): Accept a diagnostic + flag and only cache when not diagnosing errors. + (decl_satisfied_cache): Map to trees instead of bools. + (satisfy_atom): Guarantee a location for the errors, propagate complain + flags to force_rvalue, and emit errors for non-boolean constraints. + (get_normalized_constraints_and_args): New overloads. Factored out of + satisfy_constraint_expression and satisfy_declaration_constraints. + (satisfy_constraint_expression): Propagate diagnostic info to + normalization. + (satisfy_declaration_constraints): New. Factored out of + constraints_satisfied_p. + (constraint_satisfaction_value): New. Calls + satisfy_constraint_expression or satisfy_declaration_constraints. + (constraints_satisfied_p): Call constraint_satisfaction_value. + (evaluate_concept_check): Don't take tsubst_falgs_t. Replay + satisfaction if an error is encountered. + (current_failed_constraint): Moved from pt.c. + (diagnose_constraints): Call constraint_satisfaction_value. + * cp-tree.h: Update declarations. + * pt.c (current_failed_constraint): Moved to constraint.cc. + * semantics.c (finish_id_expression_1): Remove a duplicate case. + 2019-11-27 Jakub Jelinek PR c++/92524 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index f648b1d8e4c..32d929b82f3 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5649,7 +5649,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, internal_error ("unexpected template-id %qE", t); if (!processing_template_decl) - return satisfy_constraint_expression (t); + return evaluate_concept_check (t, tf_warning_or_error); else *non_constant_p = true; return t; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 171ca4e0ed4..d29c33a9bbf 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -849,10 +849,12 @@ get_normalized_constraints_from_decl (tree d, bool diag = false) /* Returns the normal form of TMPL's definition. */ static tree -normalize_concept_definition (tree tmpl) +normalize_concept_definition (tree tmpl, bool diag = false) { - if (tree *p = hash_map_safe_get (normalized_map, tmpl)) - return *p; + if (!diag) + if (tree *p = hash_map_safe_get (normalized_map, tmpl)) + return *p; + gcc_assert (concept_definition_p (tmpl)); if (OVL_P (tmpl)) tmpl = OVL_FIRST (tmpl); @@ -860,10 +862,13 @@ normalize_concept_definition (tree tmpl) tree args = generic_targs_for (tmpl); tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl)); ++processing_template_decl; - norm_info info (tmpl, tf_none); + norm_info info (tmpl, diag ? tf_norm : tf_none); tree norm = get_normalized_constraints (def, args, info); --processing_template_decl; - hash_map_safe_put (normalized_map, tmpl, norm); + + if (!diag) + hash_map_safe_put (normalized_map, tmpl, norm); + return norm; } @@ -2231,8 +2236,8 @@ struct sat_hasher : ggc_ptr_hash /* Cache the result of satisfy_atom. */ static GTY((deletable)) hash_table *sat_cache; -/* Cache the result of constraints_satisfied_p. */ -static GTY((deletable)) hash_map *decl_satisfied_cache; +/* Cache the result of constraint_satisfaction_value. */ +static GTY((deletable)) hash_map *decl_satisfied_cache; static tree get_satisfaction (tree constr, tree args) @@ -2481,21 +2486,17 @@ satisfy_atom (tree t, tree args, subst_info info) return cache.save (boolean_false_node); } - location_t loc = cp_expr_location (expr); + location_t loc = cp_expr_loc_or_input_loc (expr); /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary, and EXPR shall be a constant expression of type bool. */ - result = force_rvalue (result, tf_error); + result = force_rvalue (result, info.complain); if (result == error_mark_node) - { - if (info.noisy ()) - inform (loc, "cannot convert constraint to rvalue"); - return cache.save (error_mark_node); - } + return cache.save (error_mark_node); if (!same_type_p (TREE_TYPE (result), boolean_type_node)) { if (info.noisy ()) - inform (loc, "constraint does not have type %"); + error_at (loc, "constraint does not have type %"); return cache.save (error_mark_node); } @@ -2560,7 +2561,7 @@ satisfy_constraint (tree t, tree args, subst_info info) static tree satisfy_associated_constraints (tree t, tree args, subst_info info) { - /* If there are no constraints then this is trivially satisfied. */ + /* If there are no constraints then this is trivially satisfied. */ if (!t) return boolean_true_node; @@ -2576,24 +2577,31 @@ satisfy_associated_constraints (tree t, tree args, subst_info info) satisfaction value. */ static tree -satisfy_constraint_expression (tree expr, tree args, subst_info info) +satisfy_constraint_expression (tree t, tree args, subst_info info) { - /* Normalize the expression before satisfaction testing. */ + if (t == error_mark_node) + return error_mark_node; + + gcc_assert (EXPR_P (t)); + + /* Get the normalized constraints. */ tree norm; - if (args == NULL_TREE && concept_check_p (expr)) + if (args == NULL_TREE && concept_check_p (t)) { - tree id = unpack_concept_check (expr); + tree id = unpack_concept_check (t); args = TREE_OPERAND (id, 1); tree tmpl = get_concept_check_template (id); - norm = normalize_concept_definition (tmpl); + norm = normalize_concept_definition (tmpl, info.noisy ()); } else - norm = normalize_constraint_expression (expr); + norm = normalize_constraint_expression (t, info.noisy ()); + + /* Perform satisfaction. */ return satisfy_constraint (norm, args, info); } -/* Used to evaluate concept checks and requires-expressions during - constant expression evaluation. */ +/* Used only to evaluate requires-expressions during constant expression + evaluation. */ tree satisfy_constraint_expression (tree expr) @@ -2602,20 +2610,10 @@ satisfy_constraint_expression (tree expr) return satisfy_constraint_expression (expr, NULL_TREE, info); } -/* True if T is satisfied for ARGS. */ - -static bool -constraint_expression_satisfied_p (tree t, tree args, subst_info info) -{ - tree r = satisfy_constraint_expression (t, args, info); - return r == boolean_true_node; -} - -static bool -constraints_satisfied_p (tree t, subst_info info) +static tree +satisfy_declaration_constraints (tree t, subst_info info) { - if (!DECL_P (t)) - return constraint_expression_satisfied_p (t, NULL_TREE, info); + gcc_assert (DECL_P (t)); /* For inherited constructors, consider the original declaration; it has the correct template information attached. */ @@ -2626,21 +2624,19 @@ constraints_satisfied_p (tree t, subst_info info) info.in_decl = t; if (info.quiet ()) - if (bool *p = hash_map_safe_get (decl_satisfied_cache, t)) - return *p; + if (tree *result = hash_map_safe_get (decl_satisfied_cache, t)) + return *result; - /* Get the constraints to check for satisfaction. This depends - on whether we're looking at a template specialization or not. */ + /* Get the normalized constraints. */ tree norm = NULL_TREE; tree args = NULL_TREE; - tree ti = DECL_TEMPLATE_INFO (t); - if (ti) + 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. */ + template arguments substituted into the declaration. */ args = TI_ARGS (ti); } else @@ -2649,44 +2645,23 @@ constraints_satisfied_p (tree t, subst_info info) norm = normalize_nontemplate_requirements (t, info.noisy ()); } - bool r = true; + tree result = boolean_true_node; if (norm) { push_access_scope (t); - tree eval = satisfy_associated_constraints (norm, args, info); + result = satisfy_associated_constraints (norm, args, info); pop_access_scope (t); - r = (eval == boolean_true_node); } if (info.quiet ()) - hash_map_safe_put (decl_satisfied_cache, t, r); - - return r; -} + hash_map_safe_put (decl_satisfied_cache, t, result); -/* Returns true if the T's constraints are satisfied, of if T is an expression, - if T is satisfied. This is used in cases where the arguments can be - determined from the declaration or expression. - - Note that T is typically a template specialization. */ - -bool -constraints_satisfied_p (tree t) -{ - subst_info info (tf_none, NULL_TREE); - return constraints_satisfied_p (t, info); + return result; } -/* Returns true if the expression or constrained declaration T is - satisfied by ARGS. In this case, we don't have a specialization - where we can cache the results (e.g., alias templates). */ - -static bool -constraints_satisfied_p (tree t, tree args, subst_info info) +static tree +satisfy_declaration_constraints (tree t, tree args, subst_info info) { - if (!DECL_P (t)) - return constraint_expression_satisfied_p (t, args, info); - /* Update the declaration for diagnostics. */ info.in_decl = t; @@ -2695,46 +2670,72 @@ constraints_satisfied_p (tree t, tree args, subst_info info) { tree pattern = DECL_TEMPLATE_RESULT (t); push_access_scope (pattern); - tree eval = satisfy_associated_constraints (norm, args, info); + tree result = satisfy_associated_constraints (norm, args, info); pop_access_scope (pattern); - return eval == boolean_true_node; + return result; } - return true; + return boolean_true_node; } +static tree +constraint_satisfaction_value (tree t, tsubst_flags_t complain) +{ + subst_info info (complain, NULL_TREE); + if (DECL_P (t)) + return satisfy_declaration_constraints (t, info); + else + return satisfy_constraint_expression (t, NULL_TREE, info); +} + +static tree +constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain) +{ + subst_info info (complain, NULL_TREE); + if (DECL_P (t)) + return satisfy_declaration_constraints (t, args, info); + else + return satisfy_constraint_expression (t, args, info); +} + +/* 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) +{ + return constraint_satisfaction_value (t, tf_none) == 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. */ + bool constraints_satisfied_p (tree t, tree args) { - subst_info info (tf_none, NULL); - return constraints_satisfied_p (t, args, info); + return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node; } -/* Evaluate a concept check of the form C, returning either TRUE - or FALSE. If ARGS contains any template parameters, this returns the - check. If satisfaction yields a hard error, diagnose the error. */ +/* Evaluate a concept check of the form C. This is only used for the + evaluation of template-ids as id-expressions. */ tree evaluate_concept_check (tree check, tsubst_flags_t complain) { - /* FIXME we ought to be able to pass complain into subst_info rather - than repeat satisfaction, but currently that will complain about - non-satisfaction as well as errors. */ if (check == error_mark_node) return error_mark_node; gcc_assert (concept_check_p (check)); - subst_info info (tf_none, NULL_TREE); - tree result = satisfy_constraint_expression (check, NULL_TREE, info); + /* Check for satisfaction without diagnostics. */ + subst_info quiet (tf_none, NULL_TREE); + tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); if (result == error_mark_node && (complain & tf_error)) - { - location_t loc = cp_expr_loc_or_input_loc (check); - error_at (loc, "concept satisfaction failed"); - info.complain = complain; - satisfy_constraint_expression (check, NULL_TREE, info); - } - + { + /* Replay the error with re-normalized requirements. */ + subst_info noisy (tf_warning_or_error, NULL_TREE); + satisfy_constraint_expression (check, NULL_TREE, noisy); + } return result; } @@ -3275,6 +3276,8 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info) } } +GTY(()) tree current_failed_constraint; + diagnosing_failed_constraint:: diagnosing_failed_constraint (tree t, tree args, bool diag) : diagnosing_error (diag) @@ -3299,11 +3302,10 @@ diagnose_constraints (location_t loc, tree t, tree args) inform (loc, "constraints not satisfied"); /* Replay satisfaction, but diagnose errors. */ - subst_info info (tf_warning_or_error, NULL_TREE); if (!args) - constraints_satisfied_p (t, info); + constraint_satisfaction_value (t, tf_warning_or_error); else - constraints_satisfied_p (t, args, info); + constraint_satisfaction_value (t, args, tf_warning_or_error); } #include "gt-cp-constraint.h" diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0da1ed4e372..4b4bc245d81 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7821,8 +7821,8 @@ 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 bool constraints_satisfied_p (tree); +extern bool constraints_satisfied_p (tree, tree); extern void clear_satisfaction_cache (); extern bool* lookup_subsumption_result (tree, tree); extern bool save_subsumption_result (tree, tree, bool); diff --git a/gcc/cp/error.c b/gcc/cp/error.c index c06776f565a..4261d3c4cc9 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -3358,6 +3358,9 @@ cp_print_error_function (diagnostic_context *context, to be wrong, so just rely on print_instantiation_full_context. */ if (current_instantiation ()) return; + /* The above is true for constraint satisfaction also. */ + if (current_failed_constraint) + return; if (diagnostic_last_function_changed (context, diagnostic)) { char *old_prefix = pp_take_prefix (context->printer); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index a11718ed41e..eb907c56385 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -28736,8 +28736,6 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx) return tsubst (parm, replacement, tf_none, NULL_TREE); } -GTY(()) tree current_failed_constraint; - /* __integer_pack(N) in a pack expansion expands to a sequence of numbers from 0..N-1. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 16180f5c1fa..6c4785c6858 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3970,16 +3970,6 @@ finish_id_expression_1 (tree id_expression, decl = baselink_for_fns (decl); } - else if (concept_check_p (decl)) - { - /* If this is a standard or variable concept check, potentially - evaluate it. Function concepts need to be called as functions, - so don't try evaluating them here. */ - tree tmpl = TREE_OPERAND (decl, 0); - tree args = TREE_OPERAND (decl, 1); - if (!function_concept_p (tmpl) && !uses_template_parms (args)) - decl = evaluate_concept_check (decl, tf_warning_or_error); - } else { if (DECL_P (decl) && DECL_NONLOCAL (decl) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2fd0ae74411..4c01d147bf7 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-11-27 Andrew Sutton + + Emit hard errors for certain satisfaction errors. + * g++.dg/concepts/pr84330.C: Update diagnostics. + * g++.dg/cpp2a/concepts-requires2.C: Likewise. + 2019-11-27 Richard Biener PR tree-optimization/92690 diff --git a/gcc/testsuite/g++.dg/concepts/pr84330.C b/gcc/testsuite/g++.dg/concepts/pr84330.C index 0c2f45602d1..ba035d02555 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84330.C +++ b/gcc/testsuite/g++.dg/concepts/pr84330.C @@ -5,7 +5,7 @@ struct A { template - requires (sizeof(T) >> 0) + requires (sizeof(T) >> 0) // { dg-error "constraint does not have type 'bool'" } void foo(T); void bar() diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C index 73cee220567..8643f46a16d 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C @@ -12,7 +12,7 @@ template constexpr fool p1() { return {}; } template constexpr fool p2() { return {}; } template -concept Bad = p1() && p2(); +concept Bad = p1() && p2(); // { dg-error "does not have type 'bool'" } template requires Bad void bad(T x) { } @@ -26,14 +26,14 @@ struct X { }; int operator==(X, X) { return 0; } template -concept C1 = (X()); +concept C1 = (X()); // { dg-error "does not have type 'bool'" } template -concept C2 = (X() == X()); +concept C2 = (X() == X()); // { dg-error "does not have type 'bool'" } template requires C1 -void h1(T) { } +void h1(T) { } template requires C2 @@ -42,12 +42,12 @@ void h2(T); void driver_3() { h1(0); // { dg-error "" } - h2(0); // { dg-error "" } + h2(0); // { dg-error "" } } // req7.C template -struct boolean_constant +struct boolean_constant { constexpr operator bool() const { return B; } }; -- 2.30.2