diff mbox series

c++: constrained variable template issues [PR98486]

Message ID 20210916131115.2330482-1-ppalka@redhat.com
State New
Headers show
Series c++: constrained variable template issues [PR98486] | expand

Commit Message

Patrick Palka Sept. 16, 2021, 1:11 p.m. UTC
This fixes some issues with constrained variable templates:

  * Constraints aren't checked when explicitly specializing a variable
    template
  * Constraints aren't attached to a static data member template at
    parse time
  * Constraints aren't propagated when (partially) instantiating a
    static data member template

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

	PR c++/98486

gcc/cp/ChangeLog:

	* decl.c (grokdeclarator): Set constraints on a static data
	member template.
	* pt.c (determine_specialization): Check constraints on a
	variable template.
	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
	static data member template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-var-templ1.C: New test.
	* g++.dg/cpp2a/concepts-var-templ1a.C: New test.
	* g++.dg/cpp2a/concepts-var-templ1b.C: New test.
---
 gcc/cp/decl.c                                     | 11 +++++++++++
 gcc/cp/pt.c                                       |  8 +++++++-
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C  |  9 +++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C | 14 ++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C | 15 +++++++++++++++
 5 files changed, 56 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C

Comments

Jason Merrill Sept. 16, 2021, 3:03 p.m. UTC | #1
On 9/16/21 09:11, Patrick Palka wrote:
> This fixes some issues with constrained variable templates:
> 
>    * Constraints aren't checked when explicitly specializing a variable
>      template
>    * Constraints aren't attached to a static data member template at
>      parse time
>    * Constraints aren't propagated when (partially) instantiating a
>      static data member template
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
> cmcstl2 and range-v3, does this look OK for trunk and perhaps 11?
> 
> 	PR c++/98486
> 
> gcc/cp/ChangeLog:
> 
> 	* decl.c (grokdeclarator): Set constraints on a static data
> 	member template.
> 	* pt.c (determine_specialization): Check constraints on a
> 	variable template.

These hunks are OK.

> 	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
> 	static data member template.

Hmm, why is this necessary?  I know we already do this for functions, 
but I don't remember why.  Don't we check satisfaction for the 
most-general template?

> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/concepts-var-templ1.C: New test.
> 	* g++.dg/cpp2a/concepts-var-templ1a.C: New test.
> 	* g++.dg/cpp2a/concepts-var-templ1b.C: New test.
> ---
>   gcc/cp/decl.c                                     | 11 +++++++++++
>   gcc/cp/pt.c                                       |  8 +++++++-
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C  |  9 +++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C | 14 ++++++++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C | 15 +++++++++++++++
>   5 files changed, 56 insertions(+), 1 deletion(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> 
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index c0f1496636f..7beac79ec25 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -13980,6 +13980,17 @@ grokdeclarator (const cp_declarator *declarator,
>   		    if (declspecs->gnu_thread_keyword_p)
>   		      SET_DECL_GNU_TLS_P (decl);
>   		  }
> +
> +		/* Set the constraints on declaration.  */
> +		bool memtmpl = (processing_template_decl
> +				> template_class_depth (current_class_type));
> +		if (memtmpl)
> +		  {
> +		    tree tmpl_reqs
> +		      = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
> +		    tree ci = build_constraints (tmpl_reqs, NULL_TREE);
> +		    set_constraints (decl, ci);
> +		  }
>   	      }
>   	    else
>   	      {
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 224dd9ebd2b..613d87f2637 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -2218,7 +2218,8 @@ determine_specialization (tree template_id,
>         targs = coerce_template_parms (parms, explicit_targs, fns,
>   				     tf_warning_or_error,
>   				     /*req_all*/true, /*use_defarg*/true);
> -      if (targs != error_mark_node)
> +      if (targs != error_mark_node
> +	  && constraints_satisfied_p (fns, targs))
>           templates = tree_cons (targs, fns, templates);
>       }
>     else for (lkp_iterator iter (fns); iter; ++iter)
> @@ -14920,6 +14921,11 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>   	    if (DECL_NAMESPACE_SCOPE_P (t))
>   	      DECL_NOT_REALLY_EXTERN (r) = 1;
>   
> +	    /* Propagate the declaration's constraints.  */
> +	    if (VAR_P (r) && DECL_CLASS_SCOPE_P (r))
> +	      if (tree ci = get_constraints (t))
> +		set_constraints (r, ci);
> +
>   	    DECL_TEMPLATE_INFO (r) = build_template_info (tmpl, argvec);
>   	    SET_DECL_IMPLICIT_INSTANTIATION (r);
>   	    if (!error_operand_p (r) || (complain & tf_error))
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
> new file mode 100644
> index 00000000000..80b48ba3a3d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
> @@ -0,0 +1,9 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +template<C<int>> int v;
> +
> +template<> int v<int>;
> +template<> int v<char>; // { dg-error "match" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
> new file mode 100644
> index 00000000000..b12d37d8b7e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
> @@ -0,0 +1,14 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +struct A {
> +  template<C<int>> static int v;
> +};
> +
> +template<> int A::v<int>;
> +template<> int A::v<char>; // { dg-error "match" }
> +
> +int x = A::v<int>;
> +int y = A::v<char>; // { dg-error "invalid" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> new file mode 100644
> index 00000000000..37d7f0fc654
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> @@ -0,0 +1,15 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +template<class T>
> +struct A {
> +  template<C<T>> static int v;
> +};
> +
> +template<> template<> int A<int>::v<int>;
> +template<> template<> int A<int>::v<char>; // { dg-error "match" }
> +
> +int x = A<int>::v<int>;
> +int y = A<int>::v<char>; // { dg-error "invalid" }
>
Patrick Palka Sept. 16, 2021, 4:44 p.m. UTC | #2
On Thu, 16 Sep 2021, Jason Merrill wrote:

> On 9/16/21 09:11, Patrick Palka wrote:
> > This fixes some issues with constrained variable templates:
> > 
> >    * Constraints aren't checked when explicitly specializing a variable
> >      template
> >    * Constraints aren't attached to a static data member template at
> >      parse time
> >    * Constraints aren't propagated when (partially) instantiating a
> >      static data member template
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
> > cmcstl2 and range-v3, does this look OK for trunk and perhaps 11?
> > 
> > 	PR c++/98486
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* decl.c (grokdeclarator): Set constraints on a static data
> > 	member template.
> > 	* pt.c (determine_specialization): Check constraints on a
> > 	variable template.
> 
> These hunks are OK.
> 
> > 	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
> > 	static data member template.
> 
> Hmm, why is this necessary?  I know we already do this for functions, but I
> don't remember why.  Don't we check satisfaction for the most-general
> template?

Ah true, it looks like propagating constraints is not strictly necessary
for satisfaction for that reason..

But propagating them seems necessary for disambiguating constrained
overloads in a class template specialization:

  template<class T>
  struct A
  {
    void f() requires true;  // #1
    void f() requires false; // #2
  };

  template struct A<int>;

Without the propagation in tsubst_function_decl, during instantiation of
A<int> we complain from add_method that #2 cannot be overloaded with #1.

But I don't think this is a probem for static data member templates
since they can't be overloaded, so indeed there's no reason to propagate
constraints on them if we tweak get_normalized_constraints_from_decl.

How does the following look?  Passes all the concepts tests so far, full
testing in progress:

-- >8 --

gcc/cp/ChangeLog:

	* constraint.cc (get_normalized_constraints_from_decl): Look up
	constraints using the most general template instead of the
	specialization.
	* decl.c (grokdeclarator): Set constraints on a static data
	member template.
	* pt.c (determine_specialization): Check constraints on a
	variable template.
	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
	static data member template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-var-templ1.C: New test.
	* g++.dg/cpp2a/concepts-var-templ1a.C: New test.
	* g++.dg/cpp2a/concepts-var-templ1b.C: New test.
---
 gcc/cp/constraint.cc                              |  8 +++++---
 gcc/cp/decl.c                                     | 11 +++++++++++
 gcc/cp/pt.c                                       |  3 ++-
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C  |  9 +++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C | 14 ++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C | 15 +++++++++++++++
 6 files changed, 56 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 1aaf1e27886..2896efdd7f2 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -918,20 +918,22 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
       tmpl = most_general_template (tmpl);
   }
 
+  d = tmpl ? tmpl : decl;
+
   /* If we're not diagnosing errors, use cached constraints, if any.  */
   if (!diag)
-    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
+    if (tree *p = hash_map_safe_get (normalized_map, d))
       return *p;
 
   tree norm = NULL_TREE;
-  if (tree ci = get_constraints (decl))
+  if (tree ci = get_constraints (d))
     {
       push_access_scope_guard pas (decl);
       norm = get_normalized_constraints_from_info (ci, tmpl, diag);
     }
 
   if (!diag)
-    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+    hash_map_safe_put<hm_ggc> (normalized_map, d, norm);
 
   return norm;
 }
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c0f1496636f..7beac79ec25 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13980,6 +13980,17 @@ grokdeclarator (const cp_declarator *declarator,
 		    if (declspecs->gnu_thread_keyword_p)
 		      SET_DECL_GNU_TLS_P (decl);
 		  }
+
+		/* Set the constraints on declaration.  */
+		bool memtmpl = (processing_template_decl
+				> template_class_depth (current_class_type));
+		if (memtmpl)
+		  {
+		    tree tmpl_reqs
+		      = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+		    tree ci = build_constraints (tmpl_reqs, NULL_TREE);
+		    set_constraints (decl, ci);
+		  }
 	      }
 	    else
 	      {
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 224dd9ebd2b..22c74da7935 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2218,7 +2218,8 @@ determine_specialization (tree template_id,
       targs = coerce_template_parms (parms, explicit_targs, fns,
 				     tf_warning_or_error,
 				     /*req_all*/true, /*use_defarg*/true);
-      if (targs != error_mark_node)
+      if (targs != error_mark_node
+	  && constraints_satisfied_p (fns, targs))
         templates = tree_cons (targs, fns, templates);
     }
   else for (lkp_iterator iter (fns); iter; ++iter)
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
new file mode 100644
index 00000000000..80b48ba3a3d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
@@ -0,0 +1,9 @@
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+template<C<int>> int v;
+
+template<> int v<int>;
+template<> int v<char>; // { dg-error "match" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
new file mode 100644
index 00000000000..b12d37d8b7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
@@ -0,0 +1,14 @@
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+struct A {
+  template<C<int>> static int v;
+};
+
+template<> int A::v<int>;
+template<> int A::v<char>; // { dg-error "match" }
+
+int x = A::v<int>;
+int y = A::v<char>; // { dg-error "invalid" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
new file mode 100644
index 00000000000..37d7f0fc654
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
@@ -0,0 +1,15 @@
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+template<class T>
+struct A {
+  template<C<T>> static int v;
+};
+
+template<> template<> int A<int>::v<int>;
+template<> template<> int A<int>::v<char>; // { dg-error "match" }
+
+int x = A<int>::v<int>;
+int y = A<int>::v<char>; // { dg-error "invalid" }
Jason Merrill Sept. 16, 2021, 6:26 p.m. UTC | #3
On 9/16/21 12:44, Patrick Palka wrote:
> On Thu, 16 Sep 2021, Jason Merrill wrote:
> 
>> On 9/16/21 09:11, Patrick Palka wrote:
>>> This fixes some issues with constrained variable templates:
>>>
>>>     * Constraints aren't checked when explicitly specializing a variable
>>>       template
>>>     * Constraints aren't attached to a static data member template at
>>>       parse time
>>>     * Constraints aren't propagated when (partially) instantiating a
>>>       static data member template
>>>
>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
>>> cmcstl2 and range-v3, does this look OK for trunk and perhaps 11?
>>>
>>> 	PR c++/98486
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> 	* decl.c (grokdeclarator): Set constraints on a static data
>>> 	member template.
>>> 	* pt.c (determine_specialization): Check constraints on a
>>> 	variable template.
>>
>> These hunks are OK.
>>
>>> 	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
>>> 	static data member template.
>>
>> Hmm, why is this necessary?  I know we already do this for functions, but I
>> don't remember why.  Don't we check satisfaction for the most-general
>> template?
> 
> Ah true, it looks like propagating constraints is not strictly necessary
> for satisfaction for that reason..
> 
> But propagating them seems necessary for disambiguating constrained
> overloads in a class template specialization:
> 
>    template<class T>
>    struct A
>    {
>      void f() requires true;  // #1
>      void f() requires false; // #2
>    };
> 
>    template struct A<int>;
> 
> Without the propagation in tsubst_function_decl, during instantiation of
> A<int> we complain from add_method that #2 cannot be overloaded with #1.
> 
> But I don't think this is a probem for static data member templates
> since they can't be overloaded, so indeed there's no reason to propagate
> constraints on them if we tweak get_normalized_constraints_from_decl.
> 
> How does the following look?  Passes all the concepts tests so far, full
> testing in progress:

OK.

> -- >8 --
> 
> gcc/cp/ChangeLog:
> 
> 	* constraint.cc (get_normalized_constraints_from_decl): Look up
> 	constraints using the most general template instead of the
> 	specialization.
> 	* decl.c (grokdeclarator): Set constraints on a static data
> 	member template.
> 	* pt.c (determine_specialization): Check constraints on a
> 	variable template.
> 	(tsubst_decl) <case VAR_DECL>: Propagate constraints on a
> 	static data member template.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/concepts-var-templ1.C: New test.
> 	* g++.dg/cpp2a/concepts-var-templ1a.C: New test.
> 	* g++.dg/cpp2a/concepts-var-templ1b.C: New test.
> ---
>   gcc/cp/constraint.cc                              |  8 +++++---
>   gcc/cp/decl.c                                     | 11 +++++++++++
>   gcc/cp/pt.c                                       |  3 ++-
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C  |  9 +++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C | 14 ++++++++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C | 15 +++++++++++++++
>   6 files changed, 56 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 1aaf1e27886..2896efdd7f2 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -918,20 +918,22 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
>         tmpl = most_general_template (tmpl);
>     }
>   
> +  d = tmpl ? tmpl : decl;
> +
>     /* If we're not diagnosing errors, use cached constraints, if any.  */
>     if (!diag)
> -    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
> +    if (tree *p = hash_map_safe_get (normalized_map, d))
>         return *p;
>   
>     tree norm = NULL_TREE;
> -  if (tree ci = get_constraints (decl))
> +  if (tree ci = get_constraints (d))
>       {
>         push_access_scope_guard pas (decl);
>         norm = get_normalized_constraints_from_info (ci, tmpl, diag);
>       }
>   
>     if (!diag)
> -    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
> +    hash_map_safe_put<hm_ggc> (normalized_map, d, norm);
>   
>     return norm;
>   }
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index c0f1496636f..7beac79ec25 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -13980,6 +13980,17 @@ grokdeclarator (const cp_declarator *declarator,
>   		    if (declspecs->gnu_thread_keyword_p)
>   		      SET_DECL_GNU_TLS_P (decl);
>   		  }
> +
> +		/* Set the constraints on declaration.  */
> +		bool memtmpl = (processing_template_decl
> +				> template_class_depth (current_class_type));
> +		if (memtmpl)
> +		  {
> +		    tree tmpl_reqs
> +		      = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
> +		    tree ci = build_constraints (tmpl_reqs, NULL_TREE);
> +		    set_constraints (decl, ci);
> +		  }
>   	      }
>   	    else
>   	      {
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 224dd9ebd2b..22c74da7935 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -2218,7 +2218,8 @@ determine_specialization (tree template_id,
>         targs = coerce_template_parms (parms, explicit_targs, fns,
>   				     tf_warning_or_error,
>   				     /*req_all*/true, /*use_defarg*/true);
> -      if (targs != error_mark_node)
> +      if (targs != error_mark_node
> +	  && constraints_satisfied_p (fns, targs))
>           templates = tree_cons (targs, fns, templates);
>       }
>     else for (lkp_iterator iter (fns); iter; ++iter)
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
> new file mode 100644
> index 00000000000..80b48ba3a3d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
> @@ -0,0 +1,9 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +template<C<int>> int v;
> +
> +template<> int v<int>;
> +template<> int v<char>; // { dg-error "match" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
> new file mode 100644
> index 00000000000..b12d37d8b7e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
> @@ -0,0 +1,14 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +struct A {
> +  template<C<int>> static int v;
> +};
> +
> +template<> int A::v<int>;
> +template<> int A::v<char>; // { dg-error "match" }
> +
> +int x = A::v<int>;
> +int y = A::v<char>; // { dg-error "invalid" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> new file mode 100644
> index 00000000000..37d7f0fc654
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
> @@ -0,0 +1,15 @@
> +// PR c++/98486
> +// { dg-do compile { target c++20 } }
> +
> +template<class T, class U> concept C = __is_same(T, U);
> +
> +template<class T>
> +struct A {
> +  template<C<T>> static int v;
> +};
> +
> +template<> template<> int A<int>::v<int>;
> +template<> template<> int A<int>::v<char>; // { dg-error "match" }
> +
> +int x = A<int>::v<int>;
> +int y = A<int>::v<char>; // { dg-error "invalid" }
>
diff mbox series

Patch

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c0f1496636f..7beac79ec25 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13980,6 +13980,17 @@  grokdeclarator (const cp_declarator *declarator,
 		    if (declspecs->gnu_thread_keyword_p)
 		      SET_DECL_GNU_TLS_P (decl);
 		  }
+
+		/* Set the constraints on declaration.  */
+		bool memtmpl = (processing_template_decl
+				> template_class_depth (current_class_type));
+		if (memtmpl)
+		  {
+		    tree tmpl_reqs
+		      = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+		    tree ci = build_constraints (tmpl_reqs, NULL_TREE);
+		    set_constraints (decl, ci);
+		  }
 	      }
 	    else
 	      {
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 224dd9ebd2b..613d87f2637 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2218,7 +2218,8 @@  determine_specialization (tree template_id,
       targs = coerce_template_parms (parms, explicit_targs, fns,
 				     tf_warning_or_error,
 				     /*req_all*/true, /*use_defarg*/true);
-      if (targs != error_mark_node)
+      if (targs != error_mark_node
+	  && constraints_satisfied_p (fns, targs))
         templates = tree_cons (targs, fns, templates);
     }
   else for (lkp_iterator iter (fns); iter; ++iter)
@@ -14920,6 +14921,11 @@  tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	    if (DECL_NAMESPACE_SCOPE_P (t))
 	      DECL_NOT_REALLY_EXTERN (r) = 1;
 
+	    /* Propagate the declaration's constraints.  */
+	    if (VAR_P (r) && DECL_CLASS_SCOPE_P (r))
+	      if (tree ci = get_constraints (t))
+		set_constraints (r, ci);
+
 	    DECL_TEMPLATE_INFO (r) = build_template_info (tmpl, argvec);
 	    SET_DECL_IMPLICIT_INSTANTIATION (r);
 	    if (!error_operand_p (r) || (complain & tf_error))
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
new file mode 100644
index 00000000000..80b48ba3a3d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1.C
@@ -0,0 +1,9 @@ 
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+template<C<int>> int v;
+
+template<> int v<int>;
+template<> int v<char>; // { dg-error "match" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
new file mode 100644
index 00000000000..b12d37d8b7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1a.C
@@ -0,0 +1,14 @@ 
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+struct A {
+  template<C<int>> static int v;
+};
+
+template<> int A::v<int>;
+template<> int A::v<char>; // { dg-error "match" }
+
+int x = A::v<int>;
+int y = A::v<char>; // { dg-error "invalid" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
new file mode 100644
index 00000000000..37d7f0fc654
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-var-templ1b.C
@@ -0,0 +1,15 @@ 
+// PR c++/98486
+// { dg-do compile { target c++20 } }
+
+template<class T, class U> concept C = __is_same(T, U);
+
+template<class T>
+struct A {
+  template<C<T>> static int v;
+};
+
+template<> template<> int A<int>::v<int>;
+template<> template<> int A<int>::v<char>; // { dg-error "match" }
+
+int x = A<int>::v<int>;
+int y = A<int>::v<char>; // { dg-error "invalid" }