On Fri, 9 Jul 2021, Jason Merrill wrote: > On 7/9/21 4:18 PM, Patrick Palka wrote: > > On Fri, 9 Jul 2021, Patrick Palka wrote: > > > > > On Fri, 9 Jul 2021, Jason Merrill wrote: > > > > > > > On 7/9/21 3:18 PM, Patrick Palka wrote: > > > > > This adds support for declaring (class-scope) deduction guides for a > > > > > member class template. Fortunately it seems only a couple of changes > > > > > are needed in order for the existing CTAD machinery to handle them > > > > > like > > > > > any other deduction guide: we need to make sure to give them a > > > > > FUNCTION_TYPE instead of a METHOD_TYPE, and we need to avoid using a > > > > > BASELINK when looking them up. > > > > > > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK > > > > > for > > > > > trunk? > > > > > > > > > > PR c++/79501 > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > * decl.c (grokfndecl): Don't require that deduction guides are > > > > > declared at namespace scope. Check that class-scope deduction > > > > > guides have the same access as the member class template. > > > > > (grokdeclarator): Pretend class-scope deduction guides are > > > > > static. > > > > > * name-lookup.c (lookup_qualified_name): Don't use a BASELINK > > > > > for class-scope deduction guides. > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > * g++.dg/cpp1z/class-deduction92.C: New test. > > > > > * g++.dg/cpp1z/class-deduction93.C: New test. > > > > > * g++.dg/cpp1z/class-deduction94.C: New test. > > > > > --- > > > > > gcc/cp/decl.c | 17 ++++++++----- > > > > > gcc/cp/name-lookup.c | 11 +++++--- > > > > > .../g++.dg/cpp1z/class-deduction92.C | 16 ++++++++++++ > > > > > .../g++.dg/cpp1z/class-deduction93.C | 25 > > > > > +++++++++++++++++++ > > > > > .../g++.dg/cpp1z/class-deduction94.C | 19 ++++++++++++++ > > > > > 5 files changed, 79 insertions(+), 9 deletions(-) > > > > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > > > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > > > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction94.C > > > > > > > > > > diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c > > > > > index ebe1318d38d..8b8ffb7de83 100644 > > > > > --- a/gcc/cp/decl.c > > > > > +++ b/gcc/cp/decl.c > > > > > @@ -10040,12 +10040,6 @@ grokfndecl (tree ctype, > > > > > if (deduction_guide_p (decl)) > > > > > { > > > > > - if (!DECL_NAMESPACE_SCOPE_P (decl)) > > > > > - { > > > > > - error_at (location, "deduction guide %qD must be declared at > > > > > " > > > > > - "namespace scope", decl); > > > > > - return NULL_TREE; > > > > > - } > > > > > > > > Do we still reject deduction guides at function scope? > > > > > > Yes, it looks like the parser doesn't even recognize them at function > > > scope: > > > > > > template<class T> struct A; > > > > > > int main() { > > > A(int) -> A<int>; > > > } > > > > > > <stdin>:4:4: error: missing template arguments before ‘(’ token > > > <stdin>:4:5: error: expected primary-expression before ‘int’ > > > <stdin>:4:13: error: invalid use of ‘struct A<int>’ > > > > > > Deduction guide templates are also still rejected (as with all templates > > > at > > > function scope). > > > > > > > > > > > > tree type = TREE_TYPE (DECL_NAME (decl)); > > > > > if (in_namespace == NULL_TREE > > > > > && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type)) > > > > > @@ -10055,6 +10049,13 @@ grokfndecl (tree ctype, > > > > > inform (location_of (type), " declared here"); > > > > > return NULL_TREE; > > > > > } > > > > > + if (DECL_CLASS_SCOPE_P (decl) > > > > > + && current_access_specifier != declared_access (TYPE_NAME > > > > > (type))) > > > > > + { > > > > > + error_at (location, "deduction guide %qD must have the same > > > > > access " > > > > > + "as %qT", decl, type); > > > > > + inform (location_of (type), " declared here"); > > > > > + } > > > > > if (funcdef_flag) > > > > > error_at (location, > > > > > "deduction guide %qD must not have a function body", > > > > > decl); > > > > > @@ -12035,6 +12036,10 @@ grokdeclarator (const cp_declarator > > > > > *declarator, > > > > > storage_class = declspecs->storage_class; > > > > > if (storage_class == sc_static) > > > > > staticp = 1 + (decl_context == FIELD); > > > > > + else if (decl_context == FIELD && sfk == sfk_deduction_guide) > > > > > + /* Treat class-scope deduction guides as static member functions > > > > > + so that they get a FUNCTION_TYPE instead of a METHOD_TYPE. */ > > > > > + staticp = 2; > > > > > if (virtualp) > > > > > { > > > > > diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c > > > > > index 1be5f3da6d5..089bca1d471 100644 > > > > > --- a/gcc/cp/name-lookup.c > > > > > +++ b/gcc/cp/name-lookup.c > > > > > @@ -7110,9 +7110,14 @@ lookup_qualified_name (tree scope, tree name, > > > > > LOOK_want want, bool complain) > > > > > else if (cxx_dialect != cxx98 && TREE_CODE (scope) == > > > > > ENUMERAL_TYPE) > > > > > t = lookup_enumerator (scope, name); > > > > > else if (is_class_type (scope, complain)) > > > > > - t = lookup_member (scope, name, 2, bool (want & LOOK_want::TYPE), > > > > > - tf_warning_or_error); > > > > > - > > > > > + { > > > > > + t = lookup_member (scope, name, 2, bool (want & > > > > > LOOK_want::TYPE), > > > > > + tf_warning_or_error); > > > > > + if (t && dguide_name_p (name)) > > > > > + /* Since class-scope deduction guides aren't really member > > > > > functions, > > > > > + don't use a BASELINK for them. */ > > > > > + t = MAYBE_BASELINK_FUNCTIONS (t); > > > > > + } > > > > On second thought, this seems to be an awkward spot to do this > > adjustment. Maybe it's better to do it in lookup_member, or in > > deduction_guides_for (the only caller which really needs it)? > > Avoiding building the baselink in lookup_member does sound better than > stripping it again here.
That works well, like so? I also added the testcase from PR100983. Bootstrapped and regtested on x86_64-pc-linux-gnu. -- >8 -- PR c++/79501 PR c++/100983 gcc/cp/ChangeLog: * decl.c (grokfndecl): Don't require that deduction guides are declared at namespace scope. Check that class-scope deduction guides have the same access as the member class template. (grokdeclarator): Pretend class-scope deduction guides are static. * search.c (lookup_member): Don't use a BASELINK for (class-scope) deduction guides. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/class-deduction92.C: New test. * g++.dg/cpp1z/class-deduction93.C: New test. * g++.dg/cpp1z/class-deduction94.C: New test. * g++.dg/cpp1z/class-deduction95.C: New test. --- gcc/cp/decl.c | 17 ++++++++----- gcc/cp/search.c | 5 +++- .../g++.dg/cpp1z/class-deduction92.C | 16 ++++++++++++ .../g++.dg/cpp1z/class-deduction93.C | 25 +++++++++++++++++++ .../g++.dg/cpp1z/class-deduction94.C | 19 ++++++++++++++ .../g++.dg/cpp1z/class-deduction95.C | 13 ++++++++++ 6 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction93.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction94.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction95.C diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index ebe1318d38d..8b8ffb7de83 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -10040,12 +10040,6 @@ grokfndecl (tree ctype, if (deduction_guide_p (decl)) { - if (!DECL_NAMESPACE_SCOPE_P (decl)) - { - error_at (location, "deduction guide %qD must be declared at " - "namespace scope", decl); - return NULL_TREE; - } tree type = TREE_TYPE (DECL_NAME (decl)); if (in_namespace == NULL_TREE && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type)) @@ -10055,6 +10049,13 @@ grokfndecl (tree ctype, inform (location_of (type), " declared here"); return NULL_TREE; } + if (DECL_CLASS_SCOPE_P (decl) + && current_access_specifier != declared_access (TYPE_NAME (type))) + { + error_at (location, "deduction guide %qD must have the same access " + "as %qT", decl, type); + inform (location_of (type), " declared here"); + } if (funcdef_flag) error_at (location, "deduction guide %qD must not have a function body", decl); @@ -12035,6 +12036,10 @@ grokdeclarator (const cp_declarator *declarator, storage_class = declspecs->storage_class; if (storage_class == sc_static) staticp = 1 + (decl_context == FIELD); + else if (decl_context == FIELD && sfk == sfk_deduction_guide) + /* Treat class-scope deduction guides as static member functions + so that they get a FUNCTION_TYPE instead of a METHOD_TYPE. */ + staticp = 2; if (virtualp) { diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 7b183685476..af41bfe5835 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1226,7 +1226,10 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type, rval = error_mark_node; } - if (rval && is_overloaded_fn (rval)) + if (rval && is_overloaded_fn (rval) + /* Don't use a BASELINK for class-scope deduction guides since + they're not actually member functions. */ + && !dguide_name_p (name)) rval = build_baselink (rval_binfo, basetype_path, rval, (IDENTIFIER_CONV_OP_P (name) ? TREE_TYPE (name): NULL_TREE)); diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C new file mode 100644 index 00000000000..78e76a6ab7b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C @@ -0,0 +1,16 @@ +// PR c++/79501 +// { dg-do compile { target c++17 } } + +template<auto V> +struct X { + template<class T, auto> + struct B { T t; }; + + template<class T> B(T, int=V) -> B<const T, V>; + + auto foo() { return B{V}; } +}; + +X<42> x; +using type = decltype(x.foo()); +using type = X<42>::B<const int, 42>; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C new file mode 100644 index 00000000000..9d2db7a55a2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C @@ -0,0 +1,25 @@ +// PR c++/79501 +// { dg-do compile { target c++17 } } +// A variant of class-deduction78.C where List and its deduction guides are +// defined at class scope. + +using size_t = decltype(sizeof(42)); + +struct A { + template<typename T, size_t N = 0> + struct List { + T head; + List<T, N-1> tail; + }; + + template<typename T> + struct List<T, 0> {}; + + template<typename T> List(T) -> List<T, 1>; + template<typename T, size_t N> List(T, List<T, N>) -> List<T, N+1>; +}; + +int main() { + using type = decltype(A::List{0, A::List{1, A::List{2}}}); + using type = A::List<int, 3>; +} diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction94.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction94.C new file mode 100644 index 00000000000..f29ebd2c2b8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction94.C @@ -0,0 +1,19 @@ +// PR c++/79501 +// { dg-do compile { target c++17 } } + +struct X { +protected: + template<class T> + struct B { T t; }; + + template<class T> B(T) -> B<T>; +}; + +struct Y { +protected: + template<class T> + struct B { T t; }; + +private: + template<class T> B(T) -> B<T>; // { dg-error "access" } +}; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction95.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction95.C new file mode 100644 index 00000000000..afeb4a0d136 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction95.C @@ -0,0 +1,13 @@ +// PR c++/100983 +// { dg-do compile { target c++17 } } + +struct X { + template<int N> + struct Y { + template<class... Ts> Y(Ts...); + }; + + template<class... Ts> Y(Ts...) -> Y<sizeof...(Ts)>; +}; + +X::Y y{1,2,3}; -- 2.32.0.170.gd486ca60a5