diff mbox series

c++: Include the constraint parameter mapping in diagnostic constraint contexts

Message ID 20200318192115.1615832-1-ppalka@redhat.com
State New
Headers show
Series c++: Include the constraint parameter mapping in diagnostic constraint contexts | expand

Commit Message

Jeff Law via Gcc-patches March 18, 2020, 7:21 p.m. UTC
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

Comments

Jeff Law via Gcc-patches March 18, 2020, 7:26 p.m. UTC | #1
On Wed, 18 Mar 2020, Patrick Palka wrote:

> 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?
> 

Ah sorry, this is an old version of the patch.  Please consider this one
instead:

-- >8 --

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 dependent form of the constraint and the
	preferably substituted 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                              | 41 ++++++++-------------
 gcc/testsuite/g++.dg/concepts/diagnostic6.C | 14 +++++++
 4 files changed, 32 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..fc08558e9b6 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,24 @@ 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);
+  tree subst_map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE);
+  if (subst_map != error_mark_node)
+    map = subst_map;
+  if (map)
+    {
+      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" }
Jeff Law via Gcc-patches March 19, 2020, 2:31 p.m. UTC | #2
On 3/18/20 3:26 PM, Patrick Palka wrote:
> +  if (map)
> +    {
> +      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);
> +    }

Perhaps we should move the [with ] bits into pp_cxx_parameter_mapping 
rather than duplicate them here.

Jason
Jeff Law via Gcc-patches March 19, 2020, 4:50 p.m. UTC | #3
On Thu, 19 Mar 2020, Jason Merrill wrote:

> On 3/18/20 3:26 PM, Patrick Palka wrote:
> > +  if (map)
> > +    {
> > +      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);
> > +    }
> 
> Perhaps we should move the [with ] bits into pp_cxx_parameter_mapping rather
> than duplicate them here.

Like this?

-- >8 --

gcc/cp/ChangeLog:

	* cxx-pretty-print.c (pp_cxx_parameter_mapping): Make extern.  Move
	the "[with ]" bits to here from ...
	(pp_cxx_atomic_constraint): ... here.
	* cxx-pretty-print.h (pp_cxx_parameter_mapping): Declare.
	* error.c (rebuild_concept_check): Delete.
	(print_concept_check_info): Print the dependent form of the constraint and the
	preferably substituted parameter mapping alongside it.

gcc/testsuite/ChangeLog:

	* g++.dg/concepts/diagnostic6.C: New test.
---
 gcc/cp/cxx-pretty-print.c                   | 12 ++++---
 gcc/cp/cxx-pretty-print.h                   |  1 +
 gcc/cp/error.c                              | 36 +++++++--------------
 gcc/testsuite/g++.dg/concepts/diagnostic6.C | 14 ++++++++
 4 files changed, 33 insertions(+), 30 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..15d8b7eb609 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -2878,9 +2878,13 @@ 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)
 {
+  pp_cxx_left_bracket (pp);
+  pp->translate_string ("with");
+  pp_cxx_whitespace (pp);
+
   for (tree p = map; p; p = TREE_CHAIN (p))
     {
       tree parm = TREE_VALUE (p);
@@ -2903,6 +2907,8 @@ pp_cxx_parameter_mapping (cxx_pretty_printer *pp, tree map)
       if (TREE_CHAIN (p) != NULL_TREE)
 	pp_cxx_separate_with (pp, ';');
     }
+
+  pp_cxx_right_bracket (pp);
 }
 
 void
@@ -2915,12 +2921,8 @@ pp_cxx_atomic_constraint (cxx_pretty_printer *pp, tree t)
   tree map = ATOMIC_CONSTR_MAP (t);
   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);
    }
 }
 
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..9f19e9bbaa4 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,19 @@ 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);
+  if (map)
+    {
+      tree subst_map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE);
+      pp_cxx_whitespace (pp);
+      pp_cxx_parameter_mapping (pp, (subst_map != error_mark_node
+				     ? subst_map : map));
+    }
+  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" }
Jeff Law via Gcc-patches March 19, 2020, 8:24 p.m. UTC | #4
On 3/19/20 12:50 PM, Patrick Palka wrote:
> On Thu, 19 Mar 2020, Jason Merrill wrote:
> 
>> On 3/18/20 3:26 PM, Patrick Palka wrote:
>>> +  if (map)
>>> +    {
>>> +      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);
>>> +    }
>>
>> Perhaps we should move the [with ] bits into pp_cxx_parameter_mapping rather
>> than duplicate them here.
> 
> Like this?
> 
> -- >8 --
> 
> gcc/cp/ChangeLog:
> 
> 	* cxx-pretty-print.c (pp_cxx_parameter_mapping): Make extern.  Move
> 	the "[with ]" bits to here from ...
> 	(pp_cxx_atomic_constraint): ... here.
> 	* cxx-pretty-print.h (pp_cxx_parameter_mapping): Declare.
> 	* error.c (rebuild_concept_check): Delete.
> 	(print_concept_check_info): Print the dependent form of the constraint and the
> 	preferably substituted parameter mapping alongside it.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/concepts/diagnostic6.C: New test.
> ---
>   gcc/cp/cxx-pretty-print.c                   | 12 ++++---
>   gcc/cp/cxx-pretty-print.h                   |  1 +
>   gcc/cp/error.c                              | 36 +++++++--------------
>   gcc/testsuite/g++.dg/concepts/diagnostic6.C | 14 ++++++++
>   4 files changed, 33 insertions(+), 30 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..15d8b7eb609 100644
> --- a/gcc/cp/cxx-pretty-print.c
> +++ b/gcc/cp/cxx-pretty-print.c
> @@ -2878,9 +2878,13 @@ 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)
>   {
> +  pp_cxx_left_bracket (pp);
> +  pp->translate_string ("with");
> +  pp_cxx_whitespace (pp);
> +
>     for (tree p = map; p; p = TREE_CHAIN (p))
>       {
>         tree parm = TREE_VALUE (p);
> @@ -2903,6 +2907,8 @@ pp_cxx_parameter_mapping (cxx_pretty_printer *pp, tree map)
>         if (TREE_CHAIN (p) != NULL_TREE)
>   	pp_cxx_separate_with (pp, ';');
>       }
> +
> +  pp_cxx_right_bracket (pp);
>   }
>   
>   void
> @@ -2915,12 +2921,8 @@ pp_cxx_atomic_constraint (cxx_pretty_printer *pp, tree t)
>     tree map = ATOMIC_CONSTR_MAP (t);
>     if (map && map != error_mark_node)
>       {
> -      pp_cxx_whitespace (pp);
> -      pp_cxx_left_bracket (pp);
> -      pp->translate_string ("with");
>         pp_cxx_whitespace (pp);

Is there a reason not to move the initial whitespace as well, like in 
dump_substitution?  OK with that change.

>         pp_cxx_parameter_mapping (pp, map);
> -      pp_cxx_right_bracket (pp);
>      }
>   }
>   
> 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..9f19e9bbaa4 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,19 @@ 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);
> +  if (map)
> +    {
> +      tree subst_map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE);
> +      pp_cxx_whitespace (pp);
> +      pp_cxx_parameter_mapping (pp, (subst_map != error_mark_node
> +				     ? subst_map : map));
> +    }
> +  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" }
>
Jeff Law via Gcc-patches March 20, 2020, 2:23 p.m. UTC | #5
On Thu, 19 Mar 2020, Jason Merrill wrote:

> On 3/19/20 12:50 PM, Patrick Palka wrote:
> > On Thu, 19 Mar 2020, Jason Merrill wrote:
> > 
> > > On 3/18/20 3:26 PM, Patrick Palka wrote:
> > > > +  if (map)
> > > > +    {
> > > > +      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);
> > > > +    }
> > > 
> > > Perhaps we should move the [with ] bits into pp_cxx_parameter_mapping
> > > rather
> > > than duplicate them here.
> > 
> > Like this?
> > 
> > -- >8 --
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* cxx-pretty-print.c (pp_cxx_parameter_mapping): Make extern.  Move
> > 	the "[with ]" bits to here from ...
> > 	(pp_cxx_atomic_constraint): ... here.
> > 	* cxx-pretty-print.h (pp_cxx_parameter_mapping): Declare.
> > 	* error.c (rebuild_concept_check): Delete.
> > 	(print_concept_check_info): Print the dependent form of the constraint
> > and the
> > 	preferably substituted parameter mapping alongside it.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/concepts/diagnostic6.C: New test.
> > ---
> >   gcc/cp/cxx-pretty-print.c                   | 12 ++++---
> >   gcc/cp/cxx-pretty-print.h                   |  1 +
> >   gcc/cp/error.c                              | 36 +++++++--------------
> >   gcc/testsuite/g++.dg/concepts/diagnostic6.C | 14 ++++++++
> >   4 files changed, 33 insertions(+), 30 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..15d8b7eb609 100644
> > --- a/gcc/cp/cxx-pretty-print.c
> > +++ b/gcc/cp/cxx-pretty-print.c
> > @@ -2878,9 +2878,13 @@ 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)
> >   {
> > +  pp_cxx_left_bracket (pp);
> > +  pp->translate_string ("with");
> > +  pp_cxx_whitespace (pp);
> > +
> >     for (tree p = map; p; p = TREE_CHAIN (p))
> >       {
> >         tree parm = TREE_VALUE (p);
> > @@ -2903,6 +2907,8 @@ pp_cxx_parameter_mapping (cxx_pretty_printer *pp, tree
> > map)
> >         if (TREE_CHAIN (p) != NULL_TREE)
> >   	pp_cxx_separate_with (pp, ';');
> >       }
> > +
> > +  pp_cxx_right_bracket (pp);
> >   }
> >     void
> > @@ -2915,12 +2921,8 @@ pp_cxx_atomic_constraint (cxx_pretty_printer *pp,
> > tree t)
> >     tree map = ATOMIC_CONSTR_MAP (t);
> >     if (map && map != error_mark_node)
> >       {
> > -      pp_cxx_whitespace (pp);
> > -      pp_cxx_left_bracket (pp);
> > -      pp->translate_string ("with");
> >         pp_cxx_whitespace (pp);
> 
> Is there a reason not to move the initial whitespace as well, like in
> dump_substitution?  OK with that change.

No good reason :) Thanks, patch committed with that change.

> 
> >         pp_cxx_parameter_mapping (pp, map);
> > -      pp_cxx_right_bracket (pp);
> >      }
> >   }
> >   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..9f19e9bbaa4 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,19 @@ 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);
> > +  if (map)
> > +    {
> > +      tree subst_map = tsubst_parameter_mapping (map, args, tf_none,
> > NULL_TREE);
> > +      pp_cxx_whitespace (pp);
> > +      pp_cxx_parameter_mapping (pp, (subst_map != error_mark_node
> > +				     ? subst_map : map));
> > +    }
> > +  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"
> > }
> > 
> 
>
diff mbox series

Patch

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" }