c++: explicit specialization and trailing requirements [PR107864]

Message ID 20221128201647.484582-1-ppalka@redhat.com
State New
Headers
Series c++: explicit specialization and trailing requirements [PR107864] |

Commit Message

Patrick Palka Nov. 28, 2022, 8:16 p.m. UTC
  Here we're crashing when using an explicit specialization of a function
template with trailing requirements ultimately because decls_match
(called indirectly from register_specialization) returns false since the
template has trailing requirements whereas the specialization doesn't.

In r12-2230-gddd25bd1a7c8f4, we fixed a similar issue concerning
template requirements instead of trailing requirements.  We could just
extend this fix to ignore trailing requirement mismatches for
explicit specializations as well, but it seems cleaner to just propagate
constraints from the specialized template to the explicit specialization
so that decls_match will naturally return true.  And it looks like
determine_specialization already does this, albeit inconsistently (only
for non-template member functions of a class template as in
cpp2a/concepts-explicit-spec4.C).

So this patch makes determine_specialization consistently propagate
constraints from the specialized template to the specialization, which
obviates the function_requirements_equivalent_p special case added by
r12-2230.  In passing use add_outermost_template_args instead of open
coding it.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?  Also tested on range-v3 and cmcstl2.

	PR c++/107864

gcc/cp/ChangeLog:

	* decl.cc (function_requirements_equivalent_p): Don't check
	DECL_TEMPLATE_SPECIALIZATION.
	* pt.cc (determine_specialization): Propagate constraints when
	specializing a function template too.  Simplify by using
	add_outermost_template_args.

gcc/testsuite/ChangeLog:

	* g++.dg/concepts/explicit-spec1a.C: New test.
---
 gcc/cp/decl.cc                                |  4 +---
 gcc/cp/pt.cc                                  | 21 +++++++++----------
 .../g++.dg/concepts/explicit-spec1a.C         | 11 ++++++++++
 3 files changed, 22 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
  

Comments

Jason Merrill Nov. 28, 2022, 11:31 p.m. UTC | #1
On 11/28/22 15:16, Patrick Palka wrote:
> Here we're crashing when using an explicit specialization of a function
> template with trailing requirements ultimately because decls_match
> (called indirectly from register_specialization) returns false since the
> template has trailing requirements whereas the specialization doesn't.
> 
> In r12-2230-gddd25bd1a7c8f4, we fixed a similar issue concerning
> template requirements instead of trailing requirements.  We could just
> extend this fix to ignore trailing requirement mismatches for
> explicit specializations as well, but it seems cleaner to just propagate
> constraints from the specialized template to the explicit specialization
> so that decls_match will naturally return true.  And it looks like
> determine_specialization already does this, albeit inconsistently (only
> for non-template member functions of a class template as in
> cpp2a/concepts-explicit-spec4.C).
> 
> So this patch makes determine_specialization consistently propagate
> constraints from the specialized template to the specialization, which
> obviates the function_requirements_equivalent_p special case added by
> r12-2230.  In passing use add_outermost_template_args instead of open
> coding it.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?  Also tested on range-v3 and cmcstl2.
> 
> 	PR c++/107864
> 
> gcc/cp/ChangeLog:
> 
> 	* decl.cc (function_requirements_equivalent_p): Don't check
> 	DECL_TEMPLATE_SPECIALIZATION.
> 	* pt.cc (determine_specialization): Propagate constraints when
> 	specializing a function template too.  Simplify by using
> 	add_outermost_template_args.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/concepts/explicit-spec1a.C: New test.
> ---
>   gcc/cp/decl.cc                                |  4 +---
>   gcc/cp/pt.cc                                  | 21 +++++++++----------
>   .../g++.dg/concepts/explicit-spec1a.C         | 11 ++++++++++
>   3 files changed, 22 insertions(+), 14 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
> 
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 544efdc9914..238e72f90da 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -956,9 +956,7 @@ static bool
>   function_requirements_equivalent_p (tree newfn, tree oldfn)
>   {
>     /* In the concepts TS, the combined constraints are compared.  */
> -  if (cxx_dialect < cxx20
> -      && (DECL_TEMPLATE_SPECIALIZATION (newfn)
> -	  <= DECL_TEMPLATE_SPECIALIZATION (oldfn)))
> +  if (cxx_dialect < cxx20)

This should probably check flag_concepts_ts instead of cxx_dialect.

OK with that change.

>       {
>         tree ci1 = get_constraints (oldfn);
>         tree ci2 = get_constraints (newfn);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index fbf498ad16a..e677e9d1b38 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -2482,17 +2482,16 @@ determine_specialization (tree template_id,
>       }
>   
>     /* It was a specialization of a template.  */
> -  targs = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (TREE_VALUE (templates)));
> -  if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (targs))
> -    {
> -      *targs_out = copy_node (targs);
> -      SET_TMPL_ARGS_LEVEL (*targs_out,
> -			   TMPL_ARGS_DEPTH (*targs_out),
> -			   TREE_PURPOSE (templates));
> -    }
> -  else
> -    *targs_out = TREE_PURPOSE (templates);
> -  return TREE_VALUE (templates);
> +  tree tmpl = TREE_VALUE (templates);
> +  targs = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl));
> +  targs = add_outermost_template_args (targs, TREE_PURPOSE (templates));
> +  *targs_out = targs;
> +
> +  /* Propagate the template's constraints to the declaration.  */
> +  if (tsk != tsk_template)
> +    set_constraints (decl, get_constraints (tmpl));
> +
> +  return tmpl;
>   }
>   
>   /* Returns a chain of parameter types, exactly like the SPEC_TYPES,
> diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C b/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
> new file mode 100644
> index 00000000000..ec678740cb8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
> @@ -0,0 +1,11 @@
> +// A version of explicit-spec1.C where the template g has trailing instead of
> +// template requirements.
> +// PR c++/107864
> +// { dg-do compile { target concepts } }
> +
> +template<typename T> concept C = __is_class(T);
> +struct Y { int n; } y;
> +template<class T> void g(T) requires C<T> { }
> +int called;
> +template<> void g(Y) { called = 3; }
> +int main() { g(y); }
  

Patch

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 544efdc9914..238e72f90da 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -956,9 +956,7 @@  static bool
 function_requirements_equivalent_p (tree newfn, tree oldfn)
 {
   /* In the concepts TS, the combined constraints are compared.  */
-  if (cxx_dialect < cxx20
-      && (DECL_TEMPLATE_SPECIALIZATION (newfn)
-	  <= DECL_TEMPLATE_SPECIALIZATION (oldfn)))
+  if (cxx_dialect < cxx20)
     {
       tree ci1 = get_constraints (oldfn);
       tree ci2 = get_constraints (newfn);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index fbf498ad16a..e677e9d1b38 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -2482,17 +2482,16 @@  determine_specialization (tree template_id,
     }
 
   /* It was a specialization of a template.  */
-  targs = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (TREE_VALUE (templates)));
-  if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (targs))
-    {
-      *targs_out = copy_node (targs);
-      SET_TMPL_ARGS_LEVEL (*targs_out,
-			   TMPL_ARGS_DEPTH (*targs_out),
-			   TREE_PURPOSE (templates));
-    }
-  else
-    *targs_out = TREE_PURPOSE (templates);
-  return TREE_VALUE (templates);
+  tree tmpl = TREE_VALUE (templates);
+  targs = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl));
+  targs = add_outermost_template_args (targs, TREE_PURPOSE (templates));
+  *targs_out = targs;
+
+  /* Propagate the template's constraints to the declaration.  */
+  if (tsk != tsk_template)
+    set_constraints (decl, get_constraints (tmpl));
+
+  return tmpl;
 }
 
 /* Returns a chain of parameter types, exactly like the SPEC_TYPES,
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C b/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
new file mode 100644
index 00000000000..ec678740cb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec1a.C
@@ -0,0 +1,11 @@ 
+// A version of explicit-spec1.C where the template g has trailing instead of
+// template requirements.
+// PR c++/107864
+// { dg-do compile { target concepts } }
+
+template<typename T> concept C = __is_class(T);
+struct Y { int n; } y;
+template<class T> void g(T) requires C<T> { }
+int called;
+template<> void g(Y) { called = 3; }
+int main() { g(y); }