Commit fddfd3ce by Jason Merrill

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  <jason@redhat.com>

	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.
parent 5c161741
2020-03-24 Jason Merrill <jason@redhat.com> 2020-03-24 Jason Merrill <jason@redhat.com>
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 <jason@redhat.com>
* pt.c (any_template_parm_r): Look into the type of a non-type * pt.c (any_template_parm_r): Look into the type of a non-type
template parm. template parm.
......
...@@ -2004,6 +2004,11 @@ tsubst_nested_requirement (tree t, tree args, subst_info info) ...@@ -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. */ /* Ensure that we're in an evaluation context prior to satisfaction. */
tree norm = TREE_VALUE (TREE_TYPE (t)); tree norm = TREE_VALUE (TREE_TYPE (t));
tree result = satisfy_constraint (norm, args, info); 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) if (result != boolean_true_node)
return error_mark_node; return error_mark_node;
return result; return result;
...@@ -2489,7 +2494,7 @@ get_mapped_args (tree map) ...@@ -2489,7 +2494,7 @@ get_mapped_args (tree map)
return args; 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. */ /* Compute the satisfaction of an atomic constraint. */
...@@ -2534,8 +2539,6 @@ satisfy_atom (tree t, tree args, subst_info info) ...@@ -2534,8 +2539,6 @@ satisfy_atom (tree t, tree args, subst_info info)
return cache.save (boolean_false_node); 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, /* [17.4.1.2] ... lvalue-to-rvalue conversion is performed as necessary,
and EXPR shall be a constant expression of type bool. */ and EXPR shall be a constant expression of type bool. */
result = force_rvalue (result, info.complain); result = force_rvalue (result, info.complain);
...@@ -2544,14 +2547,22 @@ satisfy_atom (tree t, tree args, subst_info info) ...@@ -2544,14 +2547,22 @@ satisfy_atom (tree t, tree args, subst_info info)
if (!same_type_p (TREE_TYPE (result), boolean_type_node)) if (!same_type_p (TREE_TYPE (result), boolean_type_node))
{ {
if (info.noisy ()) if (info.noisy ())
error_at (loc, "constraint does not have type %<bool%>"); diagnose_atomic_constraint (t, map, result, info);
return cache.save (error_mark_node); return cache.save (error_mark_node);
} }
/* Compute the value of the constraint. */ /* 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 ()) if (result == boolean_false_node && info.noisy ())
diagnose_atomic_constraint (t, map, info); diagnose_atomic_constraint (t, map, result, info);
return cache.save (result); return cache.save (result);
} }
...@@ -2733,20 +2744,34 @@ static tree ...@@ -2733,20 +2744,34 @@ static tree
constraint_satisfaction_value (tree t, tsubst_flags_t complain) constraint_satisfaction_value (tree t, tsubst_flags_t complain)
{ {
subst_info info (complain, NULL_TREE); subst_info info (complain, NULL_TREE);
tree r;
if (DECL_P (t)) if (DECL_P (t))
return satisfy_declaration_constraints (t, info); r = satisfy_declaration_constraints (t, info);
else 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 static tree
constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain) constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain)
{ {
subst_info info (complain, NULL_TREE); subst_info info (complain, NULL_TREE);
tree r;
if (DECL_P (t)) if (DECL_P (t))
return satisfy_declaration_constraints (t, args, info); r = satisfy_declaration_constraints (t, args, info);
else 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 /* 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) ...@@ -3033,6 +3058,9 @@ at_least_as_constrained (tree d1, tree d2)
static location_t static location_t
get_constraint_error_location (tree 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. */ /* If we have a specific location give it. */
tree expr = CONSTR_EXPR (t); tree expr = CONSTR_EXPR (t);
if (location_t loc = cp_expr_location (expr)) if (location_t loc = cp_expr_location (expr))
...@@ -3041,20 +3069,23 @@ get_constraint_error_location (tree t) ...@@ -3041,20 +3069,23 @@ get_constraint_error_location (tree t)
/* If the constraint is normalized from a requires-clause, give /* If the constraint is normalized from a requires-clause, give
the location as that of the constrained declaration. */ the location as that of the constrained declaration. */
tree cxt = CONSTR_CONTEXT (t); tree cxt = CONSTR_CONTEXT (t);
tree src = TREE_VALUE (cxt); tree src = cxt ? TREE_VALUE (cxt) : NULL_TREE;
if (!src) if (!src)
/* TODO: This only happens for constrained non-template declarations. */ /* 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); return DECL_SOURCE_LOCATION (src);
/* Otherwise, give the location as the defining concept. */ /* Otherwise, give the location as the defining concept. */
gcc_assert (concept_check_p (src)); else if (concept_check_p (src))
tree id = unpack_concept_check (src); {
tree tmpl = TREE_OPERAND (id, 0); tree id = unpack_concept_check (src);
if (OVL_P (tmpl)) tree tmpl = TREE_OPERAND (id, 0);
tmpl = OVL_FIRST (tmpl); if (OVL_P (tmpl))
return DECL_SOURCE_LOCATION (tmpl); tmpl = OVL_FIRST (tmpl);
return DECL_SOURCE_LOCATION (tmpl);
}
return input_location;
} }
/* Emit a diagnostic for a failed trait. */ /* Emit a diagnostic for a failed trait. */
...@@ -3302,7 +3333,7 @@ diagnose_requires_expr (tree expr, tree map, tree in_decl) ...@@ -3302,7 +3333,7 @@ diagnose_requires_expr (tree expr, tree map, tree in_decl)
with the instantiated parameter mapping MAP. */ with the instantiated parameter mapping MAP. */
static void 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 /* If the constraint is already ill-formed, we've previously diagnosed
the reason. We should still say why the constraints aren't satisfied. */ 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) ...@@ -3338,7 +3369,11 @@ diagnose_atomic_constraint (tree t, tree map, subst_info info)
default: default:
tree a = copy_node (t); tree a = copy_node (t);
ATOMIC_CONSTR_MAP (a) = map; ATOMIC_CONSTR_MAP (a) = map;
inform (loc, "the expression %qE evaluated to %<false%>", a); if (!same_type_p (TREE_TYPE (result), boolean_type_node))
error_at (loc, "constraint %qE has type %qT, not %<bool%>",
a, TREE_TYPE (result));
else
inform (loc, "the expression %qE evaluated to %<false%>", a);
ggc_free (a); ggc_free (a);
} }
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
struct A struct A
{ {
template<typename T> template<typename T>
requires (sizeof(T) >> 0) // { dg-error "constraint does not have type 'bool'" } requires (sizeof(T) >> 0) // { dg-error "bool" }
void foo(T); void foo(T);
void bar() void bar()
......
// PR c++/94186
// { dg-do compile { target concepts } }
template <typename T>
struct is_small
{
enum { value = sizeof(T) <= 4 };
};
template <typename T>
requires is_small<T>::value // { dg-error "bool" }
void fun(T) {}
template <typename T>
void fun(T) {}
int main()
{
fun(1); // { dg-message "" }
}
...@@ -41,8 +41,8 @@ template<int N> requires N == 0 struct S2 { }; // { dg-error "does not have type ...@@ -41,8 +41,8 @@ template<int N> requires N == 0 struct S2 { }; // { dg-error "does not have type
template<int N> requires (N == 0) struct S3 { }; // OK template<int N> requires (N == 0) struct S3 { }; // OK
template<typename T, T X> requires X struct S4 { }; // OK template<typename T, T X> requires X struct S4 { }; // { dg-error "bool" }
S4<int, 0> x1; // { dg-error "template constraint failure|does not have type" } S4<int, 0> x1; // { dg-error "template constraint failure" }
S4<bool, true> x2; // OK S4<bool, true> x2; // OK
S4<bool, false> x3; // { dg-error "template constraint failure" } S4<bool, false> x3; // { dg-error "template constraint failure" }
......
...@@ -12,13 +12,13 @@ template<typename T> constexpr fool p1() { return {}; } ...@@ -12,13 +12,13 @@ template<typename T> constexpr fool p1() { return {}; }
template<typename T> constexpr fool p2() { return {}; } template<typename T> constexpr fool p2() { return {}; }
template<typename T> template<typename T>
concept Bad = p1<T>() && p2<T>(); // { dg-error "does not have type 'bool'" } concept Bad = p1<T>() && p2<T>(); // { dg-error "bool" }
template<typename T> requires Bad<T> void bad(T x) { } template<typename T> requires Bad<T> void bad(T x) { }
void driver_2() void driver_2()
{ {
bad(0); // { dg-error "" } bad(0); // { dg-message "" }
} }
// req6.C // req6.C
...@@ -26,10 +26,10 @@ struct X { }; ...@@ -26,10 +26,10 @@ struct X { };
int operator==(X, X) { return 0; } int operator==(X, X) { return 0; }
template<typename T> template<typename T>
concept C1 = (X()); // { dg-error "does not have type 'bool'" } concept C1 = (X()); // { dg-error "bool" }
template<typename T> template<typename T>
concept C2 = (X() == X()); // { dg-error "does not have type 'bool'" } concept C2 = (X() == X()); // { dg-error "bool" }
template<typename T> template<typename T>
requires C1<T> requires C1<T>
...@@ -41,8 +41,8 @@ void h2(T); ...@@ -41,8 +41,8 @@ void h2(T);
void driver_3() void driver_3()
{ {
h1(0); // { dg-error "" } h1(0); // { dg-message "" }
h2(0); // { dg-error "" } h2(0); // { dg-message "" }
} }
// req7.C // req7.C
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment