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" } -- 2.33.0.363.g4c719308ce