When diagnosing a constraint error, we currently try to print the constraint
inside a diagnostic constraint context with its template arguments substituted
in.  If substitution fails, then we instead just print the dependent
form, as in the the test case below:

  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: error: static assertion 
failed
     14 | static_assert(E<int>); // { dg-error "static assertion failed|not a 
class" }
        |               ^~~~~~
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: note: constraints not 
satisfied
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:4:11:   required for the 
satisfaction of ‘C<T>’
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:8:11:   required for the 
satisfaction of ‘D<typename T::type>’
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: error: ‘int’ is not a 
class, struct, or union type

But printing just the dependent form sometimes makes it difficult to decipher
the diagnostic.  In the above example, for instance, there's no indication of
how the template argument 'int' relates to either of the 'T's.

This patch improves the situation by changing these diagnostics to always print
the dependent form of the constraint, and alongside it the (preferably
substituted) constraint parameter mapping.  So with the same test case below we
now get:

  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: error: static assertion 
failed
     14 | static_assert(E<int>); // { dg-error "static assertion failed|not a 
class" }
        |               ^~~~~~
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: note: constraints not 
satisfied
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:4:11:   required for the 
satisfaction of ‘C<T>’ [with T = typename T::type]
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:8:11:   required for the 
satisfaction of ‘D<typename T::type>’ [with T = int]
  gcc/testsuite/g++.dg/concepts/diagnostic6.C:14:15: error: ‘int’ is not a 
class, struct, or union type

This change arguably makes it easier to figure out what's going on whenever a
constraint fails due to substitution resulting in an invalid type rather than
failing due to the constraint evaluating to false.

Tested on x86_64-pc-linux-gnu, does this look reasonable?  I'm not sure if
printing an unsubstituted parameter mapping (like in the line 4 message above)
is always useful, but perhaps it's better than nothing?

gcc/cp/ChangeLog:

        * cxx-pretty-print.c (pp_cxx_parameter_mapping): Make extern.
        * cxx-pretty-print.h (pp_cxx_parameter_mapping): Declare.
        * error.c (rebuild_concept_check): Delete.
        (print_concept_check_info): Print the constraint uninstantiated and the
        parameter mapping alongside it.

gcc/testsuite/ChangeLog:

        * g++.dg/concepts/diagnostic6.C: New test.
---
 gcc/cp/cxx-pretty-print.c                   |  2 +-
 gcc/cp/cxx-pretty-print.h                   |  1 +
 gcc/cp/error.c                              | 39 ++++++++-------------
 gcc/testsuite/g++.dg/concepts/diagnostic6.C | 14 ++++++++
 4 files changed, 30 insertions(+), 26 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic6.C

diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 100154e400f..1e89c3f9033 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -2878,7 +2878,7 @@ pp_cxx_check_constraint (cxx_pretty_printer *pp, tree t)
 /* Output the "[with ...]" clause for a parameter mapping of an atomic
    constraint.   */
 
-static void
+void
 pp_cxx_parameter_mapping (cxx_pretty_printer *pp, tree map)
 {
   for (tree p = map; p; p = TREE_CHAIN (p))
diff --git a/gcc/cp/cxx-pretty-print.h b/gcc/cp/cxx-pretty-print.h
index 7c7347f57ba..494f3fdde59 100644
--- a/gcc/cp/cxx-pretty-print.h
+++ b/gcc/cp/cxx-pretty-print.h
@@ -112,5 +112,6 @@ void pp_cxx_conjunction (cxx_pretty_printer *, tree);
 void pp_cxx_disjunction (cxx_pretty_printer *, tree);
 void pp_cxx_constraint (cxx_pretty_printer *, tree);
 void pp_cxx_constrained_type_spec (cxx_pretty_printer *, tree);
+void pp_cxx_parameter_mapping (cxx_pretty_printer *, tree);
 
 #endif /* GCC_CXX_PRETTY_PRINT_H */
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index cc51b6ffe13..4bf835e84a1 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3680,27 +3680,6 @@ print_location (diagnostic_context *context, location_t 
loc)
                  "locus", xloc.file, xloc.line);
 }
 
-/* Instantiate the concept check for the purpose of diagnosing an error.  */
-
-static tree
-rebuild_concept_check (tree expr, tree map, tree args)
-{
-  /* Instantiate the parameter mapping for the template-id.  */
-  map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE);
-  if (map == error_mark_node)
-    return error_mark_node;
-  args = get_mapped_args (map);
-
-  /* Rebuild the template id using substituted arguments. Substituting
-     directly through the expression will trigger recursive satisfaction,
-     so don't do that.  */
-  tree id = unpack_concept_check (expr);
-  args = tsubst_template_args (TREE_OPERAND (id, 1), args, tf_none, NULL_TREE);
-  if (args == error_mark_node)
-    return error_mark_node;
-  return build_nt (TEMPLATE_ID_EXPR, TREE_OPERAND (id, 0), args);
-}
-
 static void
 print_constrained_decl_info (diagnostic_context *context, tree decl)
 {
@@ -3717,12 +3696,22 @@ print_concept_check_info (diagnostic_context *context, 
tree expr, tree map, tree
   tree tmpl = TREE_OPERAND (id, 0);
   if (OVL_P (tmpl))
     tmpl = OVL_FIRST (tmpl);
-  tree check = rebuild_concept_check (expr, map, args);
-  if (check == error_mark_node)
-    check = expr;
 
   print_location (context, DECL_SOURCE_LOCATION (tmpl));
-  pp_verbatim (context->printer, "required for the satisfaction of %qE\n", 
check);
+
+  cxx_pretty_printer *pp = (cxx_pretty_printer *)context->printer;
+  pp_verbatim (pp, "required for the satisfaction of %qE", expr);
+  map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE);
+  if (map && map != error_mark_node)
+    {
+      pp_cxx_whitespace (pp);
+      pp_cxx_left_bracket (pp);
+      pp->translate_string ("with");
+      pp_cxx_whitespace (pp);
+      pp_cxx_parameter_mapping (pp, map);
+      pp_cxx_right_bracket (pp);
+    }
+  pp_newline (pp);
 }
 
 /* Diagnose the entry point into the satisfaction error. Returns the next
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic6.C 
b/gcc/testsuite/g++.dg/concepts/diagnostic6.C
new file mode 100644
index 00000000000..06b17caccbe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic6.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+  concept C = requires (T t) { t + 0; };
+// { dg-message "satisfaction of .C<T>. .with T = typename T::type." "" { 
target *-*-* } .-1 }
+
+template<typename T>
+  concept D = C<T>;
+// { dg-message "satisfaction of .D<typename T::type>. .with T = int." "" { 
target *-*-* } .-1 }
+
+template<typename T>
+  concept E = D<typename T::type>;
+
+static_assert(E<int>); // { dg-error "static assertion failed|not a class" }
-- 
2.26.0.rc1.11.g30e9940356

Reply via email to