Ping for https://gcc.gnu.org/pipermail/gcc-patches/2024-June/654792.html
On Sun, Jun 16, 2024 at 3:41 PM Nathaniel Shead <nathanielosh...@gmail.com> wrote: > > On Sun, Jun 16, 2024 at 12:29:23PM +1000, Nathaniel Shead wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > > > This probably isn't the most efficient approach, since we need to do > > name lookup to find deduction guides for a type which will also > > potentially do a bunch of pointless lazy loading from imported modules, > > but I wasn't able to work out a better approach without completely > > reworking how deduction guides are stored and represented. This way at > > least is correct (I believe), and we can maybe revisit this in the > > future. > > > > -- >8 -- > > > > Deduction guides are represented as 'normal' functions currently, and > > have no special handling in modules. However, this causes some issues; > > by [temp.deduct.guide] a deduction guide is not found by normal name > > lookup and instead all reachable deduction guides for a class template > > should be considered, but this does not happen currently. > > > > To solve this, this patch ensures that all deduction guides are > > considered exported to ensure that they are always visible to importers > > if they are reachable. Another alternative here would be to add a new > > kind of "all reachable" flag to name lookup, but that is complicated by > > some difficulties in handling GM entities; this may be a better way to > > go if more kinds of entities end up needing this handling, however. > > > > Another issue here is that because deduction guides are "unrelated" > > functions, they will usually get discarded from the GMF, so this patch > > ensures that when finding dependencies, GMF deduction guides will also > > have bindings created. We do this in find_dependencies so that we don't > > unnecessarily create bindings for GMF deduction guides that are never > > reached; for consistency we do this for *all* deduction guides, not just > > GM ones. > > > > Finally, when merging deduction guides from multiple modules, the name > > lookup code may now return two-dimensional overload sets, so update > > callers to match. > > > > As a small drive-by improvement this patch also updates the error pretty > > printing code to add a space before the '->' when printing a deduction > > guide, so we get 'S(int) -> S<int>' instead of 'S(int)-> S<int>'. > > > > PR c++/115231 > > > > gcc/cp/ChangeLog: > > > > * error.cc (dump_function_decl): Add a space before '->' when > > printing deduction guides. > > * module.cc (depset::hash::add_binding_entity): Skip deduction > > guides here. > > (depset::hash::add_deduction_guides): New. > > (depset::hash::find_dependencies): Add deduction guide > > dependencies for a class template. > > (module_state::write_cluster): Always consider deduction guides > > as exported. > > * pt.cc (deduction_guides_for): Use 'lkp_iterator' instead of > > 'ovl_iterator'. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/modules/dguide-1_a.C: New test. > > * g++.dg/modules/dguide-1_b.C: New test. > > * g++.dg/modules/dguide-2_a.C: New test. > > * g++.dg/modules/dguide-2_b.C: New test. > > * g++.dg/modules/dguide-3_a.C: New test. > > * g++.dg/modules/dguide-3_b.C: New test. > > * g++.dg/modules/dguide-3_c.C: New test. > > > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> > > --- > > gcc/cp/error.cc | 1 + > > gcc/cp/module.cc | 60 +++++++++++++++++++++++ > > gcc/cp/pt.cc | 2 +- > > gcc/testsuite/g++.dg/modules/dguide-1_a.C | 44 +++++++++++++++++ > > gcc/testsuite/g++.dg/modules/dguide-1_b.C | 20 ++++++++ > > gcc/testsuite/g++.dg/modules/dguide-2_a.C | 24 +++++++++ > > gcc/testsuite/g++.dg/modules/dguide-2_b.C | 19 +++++++ > > gcc/testsuite/g++.dg/modules/dguide-3_a.C | 10 ++++ > > gcc/testsuite/g++.dg/modules/dguide-3_b.C | 10 ++++ > > gcc/testsuite/g++.dg/modules/dguide-3_c.C | 22 +++++++++ > > 10 files changed, 211 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-1_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-1_b.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-2_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-2_b.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-3_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-3_b.C > > create mode 100644 gcc/testsuite/g++.dg/modules/dguide-3_c.C > > > > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc > > index 171a352c85f..2fb5084320e 100644 > > --- a/gcc/cp/error.cc > > +++ b/gcc/cp/error.cc > > @@ -1866,6 +1866,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, > > int flags) > > dump_type_suffix (pp, ret, flags); > > else if (deduction_guide_p (t)) > > { > > + pp->set_padding (pp_before); > > pp_cxx_ws_string (pp, "->"); > > dump_type (pp, TREE_TYPE (TREE_TYPE (t)), flags); > > } > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > > index 6d6044af199..a2c9e151fd5 100644 > > --- a/gcc/cp/module.cc > > +++ b/gcc/cp/module.cc > > @@ -2589,6 +2589,9 @@ public: > > void add_partial_entities (vec<tree, va_gc> *); > > void add_class_entities (vec<tree, va_gc> *); > > > > + private: > > + void add_deduction_guides (tree decl); > > + > > public: > > void find_dependencies (module_state *); > > bool finalize_dependencies (); > > @@ -13127,6 +13130,11 @@ depset::hash::add_binding_entity (tree decl, > > WMB_Flags flags, void *data_) > > { > > tree inner = decl; > > > > + if (deduction_guide_p (inner)) > > + /* Ignore deduction guides here, they'll be handled within > > + find_dependencies for a class template. */ > > + return false; > > + > > if (TREE_CODE (inner) == CONST_DECL > > && TREE_CODE (DECL_CONTEXT (inner)) == ENUMERAL_TYPE) > > inner = TYPE_NAME (DECL_CONTEXT (inner)); > > @@ -13590,6 +13598,50 @@ find_pending_key (tree decl, tree *decl_p = > > nullptr) > > return ns; > > } > > > > +/* Creates bindings and dependencies for all deduction guides of > > + the given class template DECL as needed. */ > > + > > +void > > +depset::hash::add_deduction_guides (tree decl) > > +{ > > + /* Alias templates never have deduction guides. */ > > + if (DECL_ALIAS_TEMPLATE_P (decl)) > > + return; > > + > > + /* We don't need to do anything for class-scope deduction guides, > > + as they will be added as members anyway. */ > > + if (!DECL_NAMESPACE_SCOPE_P (decl)) > > + return; > > + > > + tree ns = CP_DECL_CONTEXT (decl); > > + tree name = dguide_name (decl); > > + > > + /* We always add all deduction guides with a given name at once, > > + so if there's already a binding there's nothing to do. */ > > + if (find_binding (ns, name)) > > + return; > > + > > + tree guides = lookup_qualified_name (ns, name, LOOK_want::NORMAL, > > + /*complain=*/false); > > + if (guides == error_mark_node) > > + return; > > + > > + /* We have bindings to add. */ > > + depset *binding = make_binding (ns, name); > > + add_namespace_context (binding, ns); > > + > > + depset **slot = binding_slot (ns, name, /*insert=*/true); > > + *slot = binding; > > + > > + for (lkp_iterator it (guides); it; ++it) > > + { > > + gcc_checking_assert (!TREE_VISITED (*it)); > > + depset *dep = make_dependency (*it, EK_FOR_BINDING); > > + binding->deps.safe_push (dep); > > + dep->deps.safe_push (binding); > > + } > > +} > > + > > /* Iteratively find dependencies. During the walk we may find more > > entries on the same binding that need walking. */ > > > > @@ -13649,6 +13701,10 @@ depset::hash::find_dependencies (module_state > > *module) > > } > > walker.end (); > > > > + if (!walker.is_key_order () > > + && DECL_CLASS_TEMPLATE_P (decl)) > > + add_deduction_guides (decl); > > + > > if (!walker.is_key_order () > > && TREE_CODE (decl) == TEMPLATE_DECL > > && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) > > @@ -15145,6 +15201,10 @@ module_state::write_cluster (elf_out *to, depset > > *scc[], unsigned size, > > flags |= cbf_hidden; > > else if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (bound))) > > flags |= cbf_export; > > + else if (deduction_guide_p (bound)) > > + /* Deduction guides are always exported so that they are > > + reachable whenever their class template is. */ > > + flags |= cbf_export; > > } > > > > gcc_checking_assert (DECL_P (bound)); > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 607753ae6b7..8e007a571a5 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -30784,7 +30784,7 @@ deduction_guides_for (tree tmpl, bool > > &any_dguides_p, tsubst_flags_t complain) > > else > > { > > cands = ctor_deduction_guides_for (tmpl, complain); > > - for (ovl_iterator it (guides); it; ++it) > > + for (lkp_iterator it (guides); it; ++it) > > cands = lookup_add (*it, cands); > > } > > > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-1_a.C > > b/gcc/testsuite/g++.dg/modules/dguide-1_a.C > > new file mode 100644 > > index 00000000000..834e033eae3 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-1_a.C > > @@ -0,0 +1,44 @@ > > +// PR c++/115231 > > +// { dg-additional-options "-fmodules-ts -Wno-global-module" } > > +// { dg-module-cmi M } > > + > > +module; > > + > > +template <typename T> > > +struct A { > > + template <typename U> A(U); > > +}; > > + > > +template <typename T> A(T) -> A<T>; > > + > > +export module M; > > + > > +// Exporting a GMF entity should make the deduction guides reachable. > > +export using ::A; > > + > > + > > +export template <typename T> > > +struct B { > > + template <typename U> B(U); > > +}; > > + > > +// Not exported, but should still be reachable by [temp.deduct.guide] p1. > > +B(int) -> B<double>; > > + > > + > > +// Class-scope deduction guides should be reachable as well, even if > > +// the class body was not exported. > > +export template <typename T> struct C; > > + > > +template <typename T> > > +struct C { > > + template <typename U> > > + struct I { > > + template <typename V> I(V); > > + }; > > + > > + I(int) -> I<int>; > > + > > + template <typename P> > > + I(const P*) -> I<P>; > > +}; > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-1_b.C > > b/gcc/testsuite/g++.dg/modules/dguide-1_b.C > > new file mode 100644 > > index 00000000000..97266986d8f > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-1_b.C > > @@ -0,0 +1,20 @@ > > +// PR c++/115231 > > +// { dg-additional-options "-fmodules-ts" } > > + > > +import M; > > + > > +int main() { > > + // Check that deduction guides are reachable, > > + // and that they declared the right type. > > + A a(1); > > + A<int> a2 = a; > > + > > + B b(2); > > + B<double> b2 = b; > > + > > + C<int>::I x(10); > > + C<int>::I<int> x2 = x; > > + > > + C<int>::I y("xyz"); > > + C<int>::I<char> y2 = y; > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-2_a.C > > b/gcc/testsuite/g++.dg/modules/dguide-2_a.C > > new file mode 100644 > > index 00000000000..fcd6c579813 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-2_a.C > > @@ -0,0 +1,24 @@ > > +// PR c++/115231 > > +// { dg-additional-options "-fmodules-ts -Wno-global-module" } > > +// { dg-module-cmi M } > > + > > +module; > > + > > +template <typename T> > > +struct A { > > + template <typename U> A(U); > > +}; > > +template <typename T> A(T) -> A<T>; > > + > > +export module M; > > + > > +template <typename T> > > +struct B { > > + template <typename U> B(U); > > +}; > > +B(int) -> B<int>; > > + > > +// Accessing deduction guides should be possible, > > +// even if we can't name the type directly. > > +export A<void> f(); > > +export B<void> g(); > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-2_b.C > > b/gcc/testsuite/g++.dg/modules/dguide-2_b.C > > new file mode 100644 > > index 00000000000..ca31306aea3 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-2_b.C > > @@ -0,0 +1,19 @@ > > +// PR c++/115231 > > +// { dg-additional-options "-fmodules-ts" } > > + > > +import M; > > + > > +template <typename> > > +struct U; > > + > > +template <template <typename> typename TT, typename Inner> > > +struct U<TT<Inner>> { > > + void go() { > > + TT t(10); > > + } > > +}; > > + > > +int main() { > > + U<decltype(f())>{}.go(); > > + U<decltype(g())>{}.go(); > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_a.C > > b/gcc/testsuite/g++.dg/modules/dguide-3_a.C > > new file mode 100644 > > index 00000000000..33350ce9804 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-3_a.C > > @@ -0,0 +1,10 @@ > > +// { dg-additional-options "-fmodules-ts" } > > +// { dg-module-cmi A } > > + > > +export module A; > > + > > +extern "C++" { > > + template <typename T> struct S; > > + S(int) -> S<int>; > > + S(double) -> S<double>; > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_b.C > > b/gcc/testsuite/g++.dg/modules/dguide-3_b.C > > new file mode 100644 > > index 00000000000..d23696c74bd > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-3_b.C > > @@ -0,0 +1,10 @@ > > +// { dg-additional-options "-fmodules-ts" } > > +// { dg-module-cmi B } > > + > > +export module B; > > + > > +extern "C++" { > > + template <typename T> struct S; > > + S(int) -> S<int>; > > + S(char) -> S<char>; > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_c.C > > b/gcc/testsuite/g++.dg/modules/dguide-3_c.C > > new file mode 100644 > > index 00000000000..39ae8f4aa58 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/dguide-3_c.C > > @@ -0,0 +1,22 @@ > > +// { dg-additional-options "-fmodules-ts" } > > +// Test merging deduction guides. > > + > > +import A; > > +import B; > > + > > +template <typename T> struct S { > > + template <typename U> S(U); > > +}; > > + > > +int main() { > > + S x(123); // { dg-bogus "incomplete" "" { xfail *-*-* } } > > + S<int> x2 = x; > > + > > + S y('c'); // { dg-bogus "incomplete" "" { xfail *-*-* } } > > + S<char> y2 = y; > > + > > + S z(0.5); // { dg-bogus "incomplete" "" { xfail *-*-* } } > > + S<double> z2 = z; > > +} > > + > > +S(char) -> S<double>; // { dg-error "ambiguating" } > > -- > > 2.43.2 > > > > I just realised I forgot to explain these xfails in the above commit: > this is because the return types declare a new "specialisation" that is > treated as a separate type on stream-in, rather than merging with the > existing template on instantiation. > > A simplified example of this error is: > > // a.cpp > export module A; > extern "C++" template <typename T> struct S; > export S<int> foo(); > > // main.cpp > import A; > template <typename T> struct S {}; > int main() { foo(); } > > I suspect this is related to PR c++/113814. I'm pretty sure this should > be valid code ([temp.inst] p2 doesn't seem to require instantiation in A > because a function return type doesn't require a completely-defined > object type and completeness won't affect the semantics of the program) > but I haven't looked much into how to fix this yet. > > Nathaniel