From: Jason Merrill Date: Tue, 24 Mar 2020 22:25:17 +0000 (-0400) Subject: c++: Improve handling of ill-formed constraints [PR94186]. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=fddfd3ce555965864b6116cf541f6355d2057d3d;p=gcc.git c++: Improve handling of ill-formed constraints [PR94186]. It would have been trivial to make the error for non-bool constraint in satisfy_atom unconditional, but that didn't give context for the error or printing with the dependent form and template arguments. So I changed a couple of places so that, when a hard error is encountered during quiet substitution/satisfaction, we go through again noisily; this builds up the necessary context. The similar change to tsubst_nested_requirement does not build up the necessary context; rather than try to fix that now I changed get_constraint_error_location to give up and use input_location if there's no CONSTR_CONTEXT. In the case of concepts-pr67697.C, we still have a good source location because the NESTED_REQ has a correct EXPR_LOCATION, but this patch doesn't improve context printing for this case as it does for the above. gcc/cp/ChangeLog 2020-03-24 Jason Merrill PR c++/94186 * constraint.cc (constraint_satisfaction_value): Repeat noisily on error. (tsubst_nested_requirement): Likewise. (get_constraint_error_location): Allow missing context. (diagnose_atomic_constraint): Diagnose non-bool constraint here. (satisfy_atom): Not here. Only diagnose non-constant when noisy. --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a4b132d6961..f867d0e4e1a 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2020-03-24 Jason Merrill + + PR c++/94186 + * constraint.cc (constraint_satisfaction_value): Repeat noisily on + error. + (tsubst_nested_requirement): Likewise. + (get_constraint_error_location): Allow missing context. + (diagnose_atomic_constraint): Diagnose non-bool constraint here. + (satisfy_atom): Not here. Only diagnose non-constant when noisy. + 2020-03-24 Jason Merrill * pt.c (any_template_parm_r): Look into the type of a non-type diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 5e434be815f..a86bcdf603a 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -2004,6 +2004,11 @@ tsubst_nested_requirement (tree t, tree args, subst_info info) /* Ensure that we're in an evaluation context prior to satisfaction. */ tree norm = TREE_VALUE (TREE_TYPE (t)); tree result = satisfy_constraint (norm, args, info); + if (result == error_mark_node && info.quiet ()) + { + subst_info noisy (tf_warning_or_error, info.in_decl); + satisfy_constraint (norm, args, noisy); + } if (result != boolean_true_node) return error_mark_node; return result; @@ -2489,7 +2494,7 @@ get_mapped_args (tree map) return args; } -static void diagnose_atomic_constraint (tree, tree, subst_info); +static void diagnose_atomic_constraint (tree, tree, tree, subst_info); /* Compute the satisfaction of an atomic constraint. */ @@ -2534,8 +2539,6 @@ satisfy_atom (tree t, tree args, subst_info info) return cache.save (boolean_false_node); } - location_t loc = cp_expr_loc_or_input_loc (expr); - /* [17.4.1.2] ... lvalue-to-rvalue conversion is performed as necessary, and EXPR shall be a constant expression of type bool. */ result = force_rvalue (result, info.complain); @@ -2544,14 +2547,22 @@ satisfy_atom (tree t, tree args, subst_info info) if (!same_type_p (TREE_TYPE (result), boolean_type_node)) { if (info.noisy ()) - error_at (loc, "constraint does not have type %"); + diagnose_atomic_constraint (t, map, result, info); return cache.save (error_mark_node); } /* Compute the value of the constraint. */ - result = satisfaction_value (cxx_constant_value (result)); + if (info.noisy ()) + result = cxx_constant_value (result); + else + { + result = maybe_constant_value (result); + if (!TREE_CONSTANT (result)) + result = error_mark_node; + } + result = satisfaction_value (result); if (result == boolean_false_node && info.noisy ()) - diagnose_atomic_constraint (t, map, info); + diagnose_atomic_constraint (t, map, result, info); return cache.save (result); } @@ -2733,20 +2744,34 @@ static tree constraint_satisfaction_value (tree t, tsubst_flags_t complain) { subst_info info (complain, NULL_TREE); + tree r; if (DECL_P (t)) - return satisfy_declaration_constraints (t, info); + r = satisfy_declaration_constraints (t, info); else - return satisfy_constraint_expression (t, NULL_TREE, info); + r = satisfy_constraint_expression (t, NULL_TREE, info); + if (r == error_mark_node && info.quiet () + && !(DECL_P (t) && TREE_NO_WARNING (t))) + { + constraint_satisfaction_value (t, tf_warning_or_error); + if (DECL_P (t)) + /* Avoid giving these errors again. */ + TREE_NO_WARNING (t) = true; + } + return r; } static tree constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain) { subst_info info (complain, NULL_TREE); + tree r; if (DECL_P (t)) - return satisfy_declaration_constraints (t, args, info); + r = satisfy_declaration_constraints (t, args, info); else - return satisfy_constraint_expression (t, args, info); + r = satisfy_constraint_expression (t, args, info); + if (r == error_mark_node && info.quiet ()) + constraint_satisfaction_value (t, args, tf_warning_or_error); + return r; } /* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false @@ -3033,6 +3058,9 @@ at_least_as_constrained (tree d1, tree d2) static location_t get_constraint_error_location (tree t) { + if (location_t loc = cp_expr_location (t)) + return loc; + /* If we have a specific location give it. */ tree expr = CONSTR_EXPR (t); if (location_t loc = cp_expr_location (expr)) @@ -3041,20 +3069,23 @@ get_constraint_error_location (tree t) /* If the constraint is normalized from a requires-clause, give the location as that of the constrained declaration. */ tree cxt = CONSTR_CONTEXT (t); - tree src = TREE_VALUE (cxt); + tree src = cxt ? TREE_VALUE (cxt) : NULL_TREE; if (!src) /* TODO: This only happens for constrained non-template declarations. */ - return input_location; - if (DECL_P (src)) + ; + else if (DECL_P (src)) return DECL_SOURCE_LOCATION (src); - /* Otherwise, give the location as the defining concept. */ - gcc_assert (concept_check_p (src)); - tree id = unpack_concept_check (src); - tree tmpl = TREE_OPERAND (id, 0); - if (OVL_P (tmpl)) - tmpl = OVL_FIRST (tmpl); - return DECL_SOURCE_LOCATION (tmpl); + else if (concept_check_p (src)) + { + tree id = unpack_concept_check (src); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + return DECL_SOURCE_LOCATION (tmpl); + } + + return input_location; } /* Emit a diagnostic for a failed trait. */ @@ -3302,7 +3333,7 @@ diagnose_requires_expr (tree expr, tree map, tree in_decl) with the instantiated parameter mapping MAP. */ static void -diagnose_atomic_constraint (tree t, tree map, subst_info info) +diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info) { /* If the constraint is already ill-formed, we've previously diagnosed the reason. We should still say why the constraints aren't satisfied. */ @@ -3338,7 +3369,11 @@ diagnose_atomic_constraint (tree t, tree map, subst_info info) default: tree a = copy_node (t); ATOMIC_CONSTR_MAP (a) = map; - inform (loc, "the expression %qE evaluated to %", a); + if (!same_type_p (TREE_TYPE (result), boolean_type_node)) + error_at (loc, "constraint %qE has type %qT, not %", + a, TREE_TYPE (result)); + else + inform (loc, "the expression %qE evaluated to %", a); ggc_free (a); } } diff --git a/gcc/testsuite/g++.dg/concepts/pr84330.C b/gcc/testsuite/g++.dg/concepts/pr84330.C index ba035d02555..3f5b1f405a1 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) // { dg-error "constraint does not have type 'bool'" } + requires (sizeof(T) >> 0) // { dg-error "bool" } void foo(T); void bar() diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool1.C new file mode 100644 index 00000000000..8be365dfa97 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool1.C @@ -0,0 +1,20 @@ +// PR c++/94186 +// { dg-do compile { target concepts } } + +template +struct is_small +{ + enum { value = sizeof(T) <= 4 }; +}; + +template + requires is_small::value // { dg-error "bool" } +void fun(T) {} + +template +void fun(T) {} + +int main() +{ + fun(1); // { dg-message "" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C index 62cc21dd7e1..7afd34d11fd 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C @@ -41,8 +41,8 @@ template requires N == 0 struct S2 { }; // { dg-error "does not have type template requires (N == 0) struct S3 { }; // OK -template requires X struct S4 { }; // OK -S4 x1; // { dg-error "template constraint failure|does not have type" } +template requires X struct S4 { }; // { dg-error "bool" } +S4 x1; // { dg-error "template constraint failure" } S4 x2; // OK S4 x3; // { dg-error "template constraint failure" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C index 8643f46a16d..282dba63e29 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C @@ -12,13 +12,13 @@ template constexpr fool p1() { return {}; } template constexpr fool p2() { return {}; } template -concept Bad = p1() && p2(); // { dg-error "does not have type 'bool'" } +concept Bad = p1() && p2(); // { dg-error "bool" } template requires Bad void bad(T x) { } void driver_2() { - bad(0); // { dg-error "" } + bad(0); // { dg-message "" } } // req6.C @@ -26,10 +26,10 @@ struct X { }; int operator==(X, X) { return 0; } template -concept C1 = (X()); // { dg-error "does not have type 'bool'" } +concept C1 = (X()); // { dg-error "bool" } template -concept C2 = (X() == X()); // { dg-error "does not have type 'bool'" } +concept C2 = (X() == X()); // { dg-error "bool" } template requires C1 @@ -41,8 +41,8 @@ void h2(T); void driver_3() { - h1(0); // { dg-error "" } - h2(0); // { dg-error "" } + h1(0); // { dg-message "" } + h2(0); // { dg-message "" } } // req7.C