c++: Improve handling of ill-formed constraints [PR94186].
authorJason Merrill <jason@redhat.com>
Tue, 24 Mar 2020 22:25:17 +0000 (18:25 -0400)
committerJason Merrill <jason@redhat.com>
Tue, 24 Mar 2020 22:25:18 +0000 (18:25 -0400)
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.

gcc/cp/ChangeLog
gcc/cp/constraint.cc
gcc/testsuite/g++.dg/concepts/pr84330.C
gcc/testsuite/g++.dg/cpp2a/concepts-nonbool1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C

index a4b132d6961367c9255630afd4045785507f1b8a..f867d0e4e1aa853eaf490658ce8f2c5ce320d7d8 100644 (file)
@@ -1,3 +1,13 @@
+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
index 5e434be815f70eab34076ed92ac89ea533570256..a86bcdf603a6a2905960658f48ec0a9092d1972a 100644 (file)
@@ -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 %<bool%>");
+       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 %<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);
     }
 }
index ba035d02555cee7417b8405d915532a137403a73..3f5b1f405a1b84e3b89ae6284dd2d991803f828a 100644 (file)
@@ -5,7 +5,7 @@
 struct A
 {
   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 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 (file)
index 0000000..8be365d
--- /dev/null
@@ -0,0 +1,20 @@
+// 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 "" }
+}
index 62cc21dd7e180ae236d3f156e18f3fc6c3e32d50..7afd34d11fda77a71ba42ad74fb9697b95b3b0d5 100644 (file)
@@ -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<typename T, T X> requires X struct S4 { }; // OK
-S4<int, 0> x1;      // { dg-error "template constraint failure|does not have type" }
+template<typename T, T X> requires X struct S4 { }; // { dg-error "bool" }
+S4<int, 0> x1;      // { dg-error "template constraint failure" }
 S4<bool, true> x2; // OK
 S4<bool, false> x3; // { dg-error "template constraint failure" }
 
index 8643f46a16d513bd36c369f1bc3eb48433edbf65..282dba63e293a69269ca3d512f2bc61809950d08 100644 (file)
@@ -12,13 +12,13 @@ template<typename T> constexpr fool p1() { return {}; }
 template<typename T> constexpr fool p2() { return {}; }
 
 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) { }
 
 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<typename T>
-concept C1 = (X()); // { dg-error "does not have type 'bool'" }
+concept C1 = (X()); // { dg-error "bool" }
 
 template<typename T>
-concept C2 = (X() == X()); // { dg-error "does not have type 'bool'" }
+concept C2 = (X() == X()); // { dg-error "bool" }
 
 template<typename T>
   requires C1<T>
@@ -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