c++: Some improvements to concept diagnostics
authorPatrick Palka <ppalka@redhat.com>
Thu, 20 Feb 2020 20:56:20 +0000 (15:56 -0500)
committerPatrick Palka <ppalka@redhat.com>
Wed, 26 Feb 2020 16:19:18 +0000 (11:19 -0500)
This patch improves our concept diagnostics in two ways.  First, it sets a more
precise location for the constraint expressions built in
finish_constraint_binary_op.  As a result, when a disjunction is unsatisfied we
now print e.g.

.../include/bits/range_access.h:467:2: note: neither operand of the disjunction is satisfied
  466 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  467 |  || __adl_end<_Tp>
      |  ^~~~~~~~~~~~~~~~~

instead of

.../include/bits/range_access.h:467:2: note: neither operand of the disjunction is satisfied
  467 |  || __adl_end<_Tp>
      |  ^~

Second, this patch changes diagnose_atomic_constraint to print unsatisfied
atomic constraint expressions with their template arguments.  So e.g. we now
print

cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C<Tx>)()) [with Tx = {int, long int, void}]’ evaluated to ‘false’

instead of

cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C<Tx>)())’ evaluated to ‘false’

Tested on x86_64-pc-linux-gnu, and verified that all the diagnostics emitted in
our concept tests are no worse with this patch.

gcc/cp/ChangeLog:

* constraint.cc (finish_constraint_binary_op): Set expr's location range
to the range of its operands.
(satisfy_atom): Pass MAP instead of ARGS to diagnose_atomic_constraint.
(diagnose_trait_expr): Take the instantiated parameter mapping MAP
instead of the corresponding template arguments ARGS and adjust body
accordingly.
(diagnose_requires_expr): Likewise.
(diagnose_atomic_constraint): Likewise.  When printing an atomic
constraint expression, print the instantiated parameter mapping
alongside it.
* cxx-pretty-print.cc (cxx_pretty_printer::expression)
[NONTYPE_ARGUMENT_PACK]: Print braces around a NONTYPE_ARGUMENT_PACK.
(cxx_pretty_printer::type_id): Handle TYPE_ARGUMENT_PACK.

gcc/testsuite/ChangeLog:

* g++.dg/concepts/diagnostic2.C: New test.
* g++.dg/concepts/diagnostic3.C: New test.

gcc/cp/ChangeLog
gcc/cp/constraint.cc
gcc/cp/cxx-pretty-print.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/diagnostic2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/diagnostic3.C [new file with mode: 0644]

index d34730c..5962981 100644 (file)
@@ -1,3 +1,19 @@
+2020-02-26  Patrick Palka  <ppalka@redhat.com>
+
+       * constraint.cc (finish_constraint_binary_op): Set expr's location range
+       to the range of its operands.
+       (satisfy_atom): Pass MAP instead of ARGS to diagnose_atomic_constraint.
+       (diagnose_trait_expr): Take the instantiated parameter mapping MAP
+       instead of the corresponding template arguments ARGS and adjust body
+       accordingly.
+       (diagnose_requires_expr): Likewise.
+       (diagnose_atomic_constraint): Likewise.  When printing an atomic
+       constraint expression, print the instantiated parameter mapping
+       alongside it.
+       * cxx-pretty-print.cc (cxx_pretty_printer::expression)
+       [NONTYPE_ARGUMENT_PACK]: Print braces around a NONTYPE_ARGUMENT_PACK.
+       (cxx_pretty_printer::type_id): Handle TYPE_ARGUMENT_PACK.
+
 2020-02-26  Marek Polacek  <polacek@redhat.com>
 
        PR c++/93676 - value-init crash in template.
index 58044cd..4bb4a3f 100644 (file)
@@ -155,14 +155,14 @@ finish_constraint_binary_op (location_t loc,
   if (!check_constraint_operands (loc, lhs, rhs))
     return error_mark_node;
   tree overload;
-  tree expr = build_x_binary_op (loc, code,
-                                lhs, TREE_CODE (lhs),
-                                rhs, TREE_CODE (rhs),
-                                &overload, tf_none);
+  cp_expr expr = build_x_binary_op (loc, code,
+                                   lhs, TREE_CODE (lhs),
+                                   rhs, TREE_CODE (rhs),
+                                   &overload, tf_none);
   /* When either operand is dependent, the overload set may be non-empty.  */
   if (expr == error_mark_node)
     return error_mark_node;
-  SET_EXPR_LOCATION (expr, loc);
+  expr.set_range (lhs.get_start (), rhs.get_finish ());
   return expr;
 }
 
@@ -2547,7 +2547,7 @@ satisfy_atom (tree t, tree args, subst_info info)
   /* Compute the value of the constraint.  */
   result = satisfaction_value (cxx_constant_value (result));
   if (result == boolean_false_node && info.noisy ())
-    diagnose_atomic_constraint (t, args, info);
+    diagnose_atomic_constraint (t, map, info);
 
   return cache.save (result);
 }
@@ -3056,9 +3056,10 @@ get_constraint_error_location (tree t)
 /* Emit a diagnostic for a failed trait.  */
 
 void
-diagnose_trait_expr (tree expr, tree args)
+diagnose_trait_expr (tree expr, tree map)
 {
   location_t loc = cp_expr_location (expr);
+  tree args = get_mapped_args (map);
 
   /* Build a "fake" version of the instantiated trait, so we can
      get the instantiated types from result.  */
@@ -3271,11 +3272,12 @@ diagnose_requirement (tree req, tree args, tree in_decl)
 }
 
 static void
-diagnose_requires_expr (tree expr, tree args, tree in_decl)
+diagnose_requires_expr (tree expr, tree map, tree in_decl)
 {
   local_specialization_stack stack (lss_copy);
   tree parms = TREE_OPERAND (expr, 0);
   tree body = TREE_OPERAND (expr, 1);
+  tree args = get_mapped_args (map);
 
   cp_unevaluated u;
   subst_info info (tf_warning_or_error, NULL_TREE);
@@ -3292,11 +3294,11 @@ diagnose_requires_expr (tree expr, tree args, tree in_decl)
     }
 }
 
-/* Diagnose a substitution failure in the atomic constraint T. Note that
-   ARGS have been previously instantiated through the parameter map.  */
+/* Diagnose a substitution failure in the atomic constraint T when applied
+   with the instantiated parameter mapping MAP.  */
 
 static void
-diagnose_atomic_constraint (tree t, tree args, subst_info info)
+diagnose_atomic_constraint (tree t, tree map, 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.  */
@@ -3320,17 +3322,20 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info)
   switch (TREE_CODE (expr))
     {
     case TRAIT_EXPR:
-      diagnose_trait_expr (expr, args);
+      diagnose_trait_expr (expr, map);
       break;
     case REQUIRES_EXPR:
-      diagnose_requires_expr (expr, args, info.in_decl);
+      diagnose_requires_expr (expr, map, info.in_decl);
       break;
     case INTEGER_CST:
       /* This must be either 0 or false.  */
       inform (loc, "%qE is never satisfied", expr);
       break;
     default:
-      inform (loc, "the expression %qE evaluated to %<false%>", expr);
+      tree a = copy_node (t);
+      ATOMIC_CONSTR_MAP (a) = map;
+      inform (loc, "the expression %qE evaluated to %<false%>", a);
+      ggc_free (a);
     }
 }
 
index 9e0c5fc..397bdbf 100644 (file)
@@ -1214,12 +1214,14 @@ cxx_pretty_printer::expression (tree t)
       {
        tree args = ARGUMENT_PACK_ARGS (t);
        int i, len = TREE_VEC_LENGTH (args);
+       pp_cxx_left_brace (this);
        for (i = 0; i < len; ++i)
          {
            if (i > 0)
              pp_cxx_separate_with (this, ',');
            expression (TREE_VEC_ELT (args, i));
          }
+       pp_cxx_right_brace (this);
       }
       break;
 
@@ -1839,6 +1841,21 @@ cxx_pretty_printer::type_id (tree t)
       pp_cxx_ws_string (this, "...");
       break;
 
+    case TYPE_ARGUMENT_PACK:
+      {
+       tree args = ARGUMENT_PACK_ARGS (t);
+       int len = TREE_VEC_LENGTH (args);
+       pp_cxx_left_brace (this);
+       for (int i = 0; i < len; ++i)
+         {
+           if (i > 0)
+             pp_cxx_separate_with (this, ',');
+           type_id (TREE_VEC_ELT (args, i));
+         }
+       pp_cxx_right_brace (this);
+      }
+      break;
+
     default:
       c_pretty_printer::type_id (t);
       break;
index 572989b..662ffbf 100644 (file)
@@ -1,3 +1,8 @@
+2020-02-26  Patrick Palka  <ppalka@redhat.com>
+
+       * g++.dg/concepts/diagnostic2.C: New test.
+       * g++.dg/concepts/diagnostic3.C: New test.
+
 2020-02-26  Marek Polacek  <polacek@redhat.com>
 
        PR c++/93676 - value-init crash in template.
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
new file mode 100644 (file)
index 0000000..ce51b71
--- /dev/null
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++2a } }
+// { dg-options "-fdiagnostics-show-caret" }
+
+template<typename T>
+  inline constexpr bool foo_v = false;
+
+template<typename T>
+  concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" }
+/* { dg-begin-multiline-output "" }
+   concept foo = foo_v<T> || foo_v<T&>;
+                 ~~~~~~~~~^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+template<typename T>
+  requires foo<T>
+  void bar();
+/* { dg-begin-multiline-output "" }
+   void bar();
+   { dg-end-multiline-output "" } */
+/* { dg-prune-output "~" } */
+
+void
+baz()
+{
+  bar<int>(); // { dg-error "unsatisfied constraints" }
+/* { dg-begin-multiline-output "" }
+   bar<int>();
+            ^
+   { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C
new file mode 100644 (file)
index 0000000..b4c7540
--- /dev/null
@@ -0,0 +1,29 @@
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+  inline constexpr bool foo_v = false;
+
+template<typename T>
+  concept foo = (bool)(foo_v<T> | foo_v<T&>);
+
+template<typename... Ts>
+requires (foo<Ts> && ...)
+void
+bar() // { dg-message "with Ts = .int, char... evaluated to .false." }
+{ }
+
+template<int>
+struct S { };
+
+template<int... Is>
+requires (foo<S<Is>> && ...)
+void
+baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." }
+{ }
+
+void
+baz()
+{
+  bar<int, char>(); // { dg-error "unsatisfied constraints" }
+  baz<2,3,4>(); // { dg-error "unsatisfied constraints" }
+}