Message ID | 6716451e.170a0220.359288.5a3c@mx.google.com |
---|---|
State | New |
Headers | show |
Series | c++/modules: Handle forward-declared class types | expand |
On 10/21/24 8:12 AM, Nathaniel Shead wrote: > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? OK. > -- >8 -- > > In some cases we can access members of a namespace-scope class without > ever having performed name-lookup on it; this can occur when a > forward-declaration of the class is used as a return type, for > instance, or with PIMPL. > > One possible approach would be to do name lookup in complete_type to > force lazy loading to occur, but this seems overly expensive for a > relatively rare case. Instead, this patch generalises the existing > pending-entity support to handle this case as well. > > Unfortunately this does mean that almost every class definition will be > added to the pending-entity table, and almost always unnecessarily, but > I don't see a good way to avoid this. > > gcc/cp/ChangeLog: > > * module.cc (depset::DB_IS_MEMBER_BIT): Rename to... > (depset::DB_IS_PENDING_BIT): ...this. > (depset::is_member): Remove. > (depset::is_pending_entity): New function. > (depset::hash::make_dependency): Mark definitions of > namespace-scope types as maybe-pending entities. > (depset::hash::add_class_entities): Rename DB_IS_MEMBER_BIT to > DB_IS_PENDING_BIT. > (depset::hash::find_dependencies): Use is_pending_entity > instead of is_member. > (module_state::write_pendings): Likewise; adjust comment. > > gcc/testsuite/ChangeLog: > > * g++.dg/modules/inst-4_b.C: Adjust pending-entity count. > * g++.dg/modules/member-def-1_c.C: Likewise. > * g++.dg/modules/member-def-2_c.C: Likewise. > * g++.dg/modules/tpl-spec-3_b.C: Likewise. > * g++.dg/modules/tpl-spec-4_b.C: Likewise. > * g++.dg/modules/tpl-spec-5_b.C: Likewise. > * g++.dg/modules/class-9_a.H: New test. > * g++.dg/modules/class-9_b.H: New test. > * g++.dg/modules/class-9_c.C: New test. > > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> > --- > gcc/cp/module.cc | 54 +++++++++++-------- > gcc/testsuite/g++.dg/modules/class-9_a.H | 8 +++ > gcc/testsuite/g++.dg/modules/class-9_b.H | 7 +++ > gcc/testsuite/g++.dg/modules/class-9_c.C | 10 ++++ > gcc/testsuite/g++.dg/modules/inst-4_b.C | 2 +- > gcc/testsuite/g++.dg/modules/member-def-1_c.C | 4 +- > gcc/testsuite/g++.dg/modules/member-def-2_c.C | 2 +- > gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C | 3 +- > gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C | 2 +- > gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C | 2 +- > 10 files changed, 64 insertions(+), 30 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/modules/class-9_a.H > create mode 100644 gcc/testsuite/g++.dg/modules/class-9_b.H > create mode 100644 gcc/testsuite/g++.dg/modules/class-9_c.C > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index 2dc59ce8a12..fd9b1d3bf2e 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -2329,7 +2329,7 @@ private: > DB_KIND_BIT, /* Kind of the entity. */ > DB_KIND_BITS = EK_BITS, > DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS, > - DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ > + DB_IS_PENDING_BIT, /* Is a maybe-pending entity. */ > DB_IS_INTERNAL_BIT, /* It is an (erroneous) > internal-linkage entity. */ > DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage > @@ -2407,11 +2407,14 @@ public: > } > > public: > - /* This class-member is defined here, but the class was imported. */ > - bool is_member () const > + /* This entity might be found other than by namespace-scope lookup; > + see module_state::write_pendings for more details. */ > + bool is_pending_entity () const > { > - gcc_checking_assert (get_entity_kind () == EK_DECL); > - return get_flag_bit<DB_IS_MEMBER_BIT> (); > + return (get_entity_kind () == EK_SPECIALIZATION > + || get_entity_kind () == EK_PARTIAL > + || (get_entity_kind () == EK_DECL > + && get_flag_bit<DB_IS_PENDING_BIT> ())); > } > public: > bool is_internal () const > @@ -13031,6 +13034,18 @@ depset::hash::make_dependency (tree decl, entity_kind ek) > dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); > } > } > + > + /* A namespace-scope type may be declared in one module unit > + and defined in another; make sure that we're found when > + completing the class. */ > + if (ek == EK_DECL > + && !dep->is_import () > + && dep->has_defn () > + && DECL_NAMESPACE_SCOPE_P (not_tmpl) > + && DECL_IMPLICIT_TYPEDEF_P (not_tmpl) > + /* Anonymous types can't be forward-declared. */ > + && !IDENTIFIER_ANON_P (DECL_NAME (not_tmpl))) > + dep->set_flag_bit<DB_IS_PENDING_BIT> (); > } > > if (!dep->is_import ()) > @@ -13383,9 +13398,9 @@ depset::hash::add_class_entities (vec<tree, va_gc> *class_members) > if (dep->get_entity_kind () == EK_REDIRECT) > dep = dep->deps[0]; > > - /* Only non-instantiations need marking as members. */ > + /* Only non-instantiations need marking as pendings. */ > if (dep->get_entity_kind () == EK_DECL) > - dep->set_flag_bit <DB_IS_MEMBER_BIT> (); > + dep->set_flag_bit <DB_IS_PENDING_BIT> (); > } > } > > @@ -13711,10 +13726,7 @@ depset::hash::find_dependencies (module_state *module) > walker.mark_declaration (decl, current->has_defn ()); > > if (!walker.is_key_order () > - && (item->get_entity_kind () == EK_SPECIALIZATION > - || item->get_entity_kind () == EK_PARTIAL > - || (item->get_entity_kind () == EK_DECL > - && item->is_member ()))) > + && item->is_pending_entity ()) > { > tree ns = find_pending_key (decl, nullptr); > add_namespace_context (item, ns); > @@ -15939,15 +15951,13 @@ module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm) > 'instantiated' in one module, and it'd be nice to not have to > reinstantiate it in another. > > - (c) A member classes completed elsewhere. A member class could be > - declared in one header and defined in another. We need to know to > - load the class definition before looking in it. This turns out to > - be a specific case of #b, so we can treat these the same. But it > - does highlight an issue -- there could be an intermediate import > - between the outermost containing namespace-scope class and the > - innermost being-defined member class. This is actually possible > - with all of these cases, so be aware -- we're not just talking of > - one level of import to get to the innermost namespace. > + (c) Classes completed elsewhere. A class could be declared in one > + header and defined in another. We need to know to load the class > + definition before looking in it. It does highlight an issue -- > + there could be an intermediate import between the outermost containing > + namespace-scope class and the innermost being-defined class. This is > + actually possible with all of these cases, so be aware -- we're not > + just talking of one level of import to get to the innermost namespace. > > This gets complicated fast, it took me multiple attempts to even > get something remotely working. Partially because I focussed on > @@ -16067,9 +16077,7 @@ module_state::write_pendings (elf_out *to, vec<depset *> depsets, > if (d->is_import ()) > continue; > > - if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION > - || d->get_entity_kind () == depset::EK_PARTIAL > - || (d->get_entity_kind () == depset::EK_DECL && d->is_member ()))) > + if (!d->is_pending_entity ()) > continue; > > tree key_decl = nullptr; > diff --git a/gcc/testsuite/g++.dg/modules/class-9_a.H b/gcc/testsuite/g++.dg/modules/class-9_a.H > new file mode 100644 > index 00000000000..9a0bf6323f7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/class-9_a.H > @@ -0,0 +1,8 @@ > +// { dg-additional-options "-fmodule-header" } > +// { dg-module-cmi {} } > + > +struct A; > +A* foo(); > + > +template <typename T> struct B; > +template <typename T> B<T>* bar(); > diff --git a/gcc/testsuite/g++.dg/modules/class-9_b.H b/gcc/testsuite/g++.dg/modules/class-9_b.H > new file mode 100644 > index 00000000000..931adf66081 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/class-9_b.H > @@ -0,0 +1,7 @@ > +// { dg-additional-options "-fmodule-header -fdump-lang-module" } > +// { dg-module-cmi {} } > + > +struct A { int a; }; > +template <typename T> struct B { int b; }; > + > +// { dg-final { scan-lang-dump {Pendings 2} module } } > diff --git a/gcc/testsuite/g++.dg/modules/class-9_c.C b/gcc/testsuite/g++.dg/modules/class-9_c.C > new file mode 100644 > index 00000000000..de34efdae0b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/class-9_c.C > @@ -0,0 +1,10 @@ > +// { dg-additional-options "-fmodules-ts -fmodule-lazy" } > + > +import "class-9_a.H"; > +import "class-9_b.H"; > + > +int main() { > + // Lazy loading should still find the definitions of A and B. > + int a = foo()->a; > + int b = bar<int>()->b; > +} > diff --git a/gcc/testsuite/g++.dg/modules/inst-4_b.C b/gcc/testsuite/g++.dg/modules/inst-4_b.C > index c7b02b470bd..40f9ba976db 100644 > --- a/gcc/testsuite/g++.dg/modules/inst-4_b.C > +++ b/gcc/testsuite/g++.dg/modules/inst-4_b.C > @@ -9,5 +9,5 @@ int main () > return 0; > } > > -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::TPL'} module } } > +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::TPL'} module } } > // { dg-final { scan-lang-dump {Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::TPL'} module } } > diff --git a/gcc/testsuite/g++.dg/modules/member-def-1_c.C b/gcc/testsuite/g++.dg/modules/member-def-1_c.C > index d4190a84d58..fee6f4207e3 100644 > --- a/gcc/testsuite/g++.dg/modules/member-def-1_c.C > +++ b/gcc/testsuite/g++.dg/modules/member-def-1_c.C > @@ -11,6 +11,6 @@ export auto foo () > return frob::inner (); > } > > -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } } > +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::frob'} module } } > // { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1'\n \[1\]=decl definition '::frob@foo:part1:1::inner@foo:part1:1'\n \[2\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__dt '\n( \[.\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__ct '\n)* \[6\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::inner@foo:part2:2'\n \[7\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n \[8\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n \[9\]=binding '::frob'\n} module } } > -// { dg-final { scan-lang-dump {Pendings 0} module } } > +// { dg-final { scan-lang-dump {Pendings 1} module } } > diff --git a/gcc/testsuite/g++.dg/modules/member-def-2_c.C b/gcc/testsuite/g++.dg/modules/member-def-2_c.C > index f0a193f34ce..33b1b9fe9d2 100644 > --- a/gcc/testsuite/g++.dg/modules/member-def-2_c.C > +++ b/gcc/testsuite/g++.dg/modules/member-def-2_c.C > @@ -9,7 +9,7 @@ export import :part1; > > // { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1'\n \[1\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n \[2\]=decl definition '::frob@foo:part1:1::member@foo:part1:1'\n \[3\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n \[4\]=binding '::frob'\n} module } } > // { dg-final { scan-lang-dump {Bindings 1} module } } > -// { dg-final { scan-lang-dump {Pendings 0} module } } > +// { dg-final { scan-lang-dump {Pendings 1} module } } > // { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key .matched. function_decl:'::frob@foo:part1:1::member'} module } } > > // { dg-final { scan-assembler-not {_ZN4frob6memberEv:} } } > diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C > index a7105ae6759..80534ff64a5 100644 > --- a/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C > +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C > @@ -17,7 +17,8 @@ int main () > return 0; > } > > -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } } > +// We read a pending for both '::frob' and '::frob::store'. > +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::frob'} module } } > // { dg-final { scan-lang-dump-not {Reading definition function_decl '::frob@TPL:.::store@TPL:.<int>'} module } } > > // { dg-final { scan-assembler-not {_ZN4frob5storeIiEEvT_:} } } > diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C > index 97aa251d3e0..ea8d014e88b 100644 > --- a/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C > +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C > @@ -14,4 +14,4 @@ int main () > return 0; > } > > -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } } > +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::X'} module } } > diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C > index ff3d84c1384..300f649ac27 100644 > --- a/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C > +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C > @@ -14,4 +14,4 @@ int main () > return 0; > } > > -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } } > +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::X'} module } }
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 2dc59ce8a12..fd9b1d3bf2e 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2329,7 +2329,7 @@ private: DB_KIND_BIT, /* Kind of the entity. */ DB_KIND_BITS = EK_BITS, DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS, - DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ + DB_IS_PENDING_BIT, /* Is a maybe-pending entity. */ DB_IS_INTERNAL_BIT, /* It is an (erroneous) internal-linkage entity. */ DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage @@ -2407,11 +2407,14 @@ public: } public: - /* This class-member is defined here, but the class was imported. */ - bool is_member () const + /* This entity might be found other than by namespace-scope lookup; + see module_state::write_pendings for more details. */ + bool is_pending_entity () const { - gcc_checking_assert (get_entity_kind () == EK_DECL); - return get_flag_bit<DB_IS_MEMBER_BIT> (); + return (get_entity_kind () == EK_SPECIALIZATION + || get_entity_kind () == EK_PARTIAL + || (get_entity_kind () == EK_DECL + && get_flag_bit<DB_IS_PENDING_BIT> ())); } public: bool is_internal () const @@ -13031,6 +13034,18 @@ depset::hash::make_dependency (tree decl, entity_kind ek) dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); } } + + /* A namespace-scope type may be declared in one module unit + and defined in another; make sure that we're found when + completing the class. */ + if (ek == EK_DECL + && !dep->is_import () + && dep->has_defn () + && DECL_NAMESPACE_SCOPE_P (not_tmpl) + && DECL_IMPLICIT_TYPEDEF_P (not_tmpl) + /* Anonymous types can't be forward-declared. */ + && !IDENTIFIER_ANON_P (DECL_NAME (not_tmpl))) + dep->set_flag_bit<DB_IS_PENDING_BIT> (); } if (!dep->is_import ()) @@ -13383,9 +13398,9 @@ depset::hash::add_class_entities (vec<tree, va_gc> *class_members) if (dep->get_entity_kind () == EK_REDIRECT) dep = dep->deps[0]; - /* Only non-instantiations need marking as members. */ + /* Only non-instantiations need marking as pendings. */ if (dep->get_entity_kind () == EK_DECL) - dep->set_flag_bit <DB_IS_MEMBER_BIT> (); + dep->set_flag_bit <DB_IS_PENDING_BIT> (); } } @@ -13711,10 +13726,7 @@ depset::hash::find_dependencies (module_state *module) walker.mark_declaration (decl, current->has_defn ()); if (!walker.is_key_order () - && (item->get_entity_kind () == EK_SPECIALIZATION - || item->get_entity_kind () == EK_PARTIAL - || (item->get_entity_kind () == EK_DECL - && item->is_member ()))) + && item->is_pending_entity ()) { tree ns = find_pending_key (decl, nullptr); add_namespace_context (item, ns); @@ -15939,15 +15951,13 @@ module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm) 'instantiated' in one module, and it'd be nice to not have to reinstantiate it in another. - (c) A member classes completed elsewhere. A member class could be - declared in one header and defined in another. We need to know to - load the class definition before looking in it. This turns out to - be a specific case of #b, so we can treat these the same. But it - does highlight an issue -- there could be an intermediate import - between the outermost containing namespace-scope class and the - innermost being-defined member class. This is actually possible - with all of these cases, so be aware -- we're not just talking of - one level of import to get to the innermost namespace. + (c) Classes completed elsewhere. A class could be declared in one + header and defined in another. We need to know to load the class + definition before looking in it. It does highlight an issue -- + there could be an intermediate import between the outermost containing + namespace-scope class and the innermost being-defined class. This is + actually possible with all of these cases, so be aware -- we're not + just talking of one level of import to get to the innermost namespace. This gets complicated fast, it took me multiple attempts to even get something remotely working. Partially because I focussed on @@ -16067,9 +16077,7 @@ module_state::write_pendings (elf_out *to, vec<depset *> depsets, if (d->is_import ()) continue; - if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION - || d->get_entity_kind () == depset::EK_PARTIAL - || (d->get_entity_kind () == depset::EK_DECL && d->is_member ()))) + if (!d->is_pending_entity ()) continue; tree key_decl = nullptr; diff --git a/gcc/testsuite/g++.dg/modules/class-9_a.H b/gcc/testsuite/g++.dg/modules/class-9_a.H new file mode 100644 index 00000000000..9a0bf6323f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/class-9_a.H @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +struct A; +A* foo(); + +template <typename T> struct B; +template <typename T> B<T>* bar(); diff --git a/gcc/testsuite/g++.dg/modules/class-9_b.H b/gcc/testsuite/g++.dg/modules/class-9_b.H new file mode 100644 index 00000000000..931adf66081 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/class-9_b.H @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodule-header -fdump-lang-module" } +// { dg-module-cmi {} } + +struct A { int a; }; +template <typename T> struct B { int b; }; + +// { dg-final { scan-lang-dump {Pendings 2} module } } diff --git a/gcc/testsuite/g++.dg/modules/class-9_c.C b/gcc/testsuite/g++.dg/modules/class-9_c.C new file mode 100644 index 00000000000..de34efdae0b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/class-9_c.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fmodules-ts -fmodule-lazy" } + +import "class-9_a.H"; +import "class-9_b.H"; + +int main() { + // Lazy loading should still find the definitions of A and B. + int a = foo()->a; + int b = bar<int>()->b; +} diff --git a/gcc/testsuite/g++.dg/modules/inst-4_b.C b/gcc/testsuite/g++.dg/modules/inst-4_b.C index c7b02b470bd..40f9ba976db 100644 --- a/gcc/testsuite/g++.dg/modules/inst-4_b.C +++ b/gcc/testsuite/g++.dg/modules/inst-4_b.C @@ -9,5 +9,5 @@ int main () return 0; } -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::TPL'} module } } +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::TPL'} module } } // { dg-final { scan-lang-dump {Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::TPL'} module } } diff --git a/gcc/testsuite/g++.dg/modules/member-def-1_c.C b/gcc/testsuite/g++.dg/modules/member-def-1_c.C index d4190a84d58..fee6f4207e3 100644 --- a/gcc/testsuite/g++.dg/modules/member-def-1_c.C +++ b/gcc/testsuite/g++.dg/modules/member-def-1_c.C @@ -11,6 +11,6 @@ export auto foo () return frob::inner (); } -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } } +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::frob'} module } } // { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1'\n \[1\]=decl definition '::frob@foo:part1:1::inner@foo:part1:1'\n \[2\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__dt '\n( \[.\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__ct '\n)* \[6\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::inner@foo:part2:2'\n \[7\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n \[8\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n \[9\]=binding '::frob'\n} module } } -// { dg-final { scan-lang-dump {Pendings 0} module } } +// { dg-final { scan-lang-dump {Pendings 1} module } } diff --git a/gcc/testsuite/g++.dg/modules/member-def-2_c.C b/gcc/testsuite/g++.dg/modules/member-def-2_c.C index f0a193f34ce..33b1b9fe9d2 100644 --- a/gcc/testsuite/g++.dg/modules/member-def-2_c.C +++ b/gcc/testsuite/g++.dg/modules/member-def-2_c.C @@ -9,7 +9,7 @@ export import :part1; // { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1'\n \[1\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n \[2\]=decl definition '::frob@foo:part1:1::member@foo:part1:1'\n \[3\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n \[4\]=binding '::frob'\n} module } } // { dg-final { scan-lang-dump {Bindings 1} module } } -// { dg-final { scan-lang-dump {Pendings 0} module } } +// { dg-final { scan-lang-dump {Pendings 1} module } } // { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key .matched. function_decl:'::frob@foo:part1:1::member'} module } } // { dg-final { scan-assembler-not {_ZN4frob6memberEv:} } } diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C index a7105ae6759..80534ff64a5 100644 --- a/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C @@ -17,7 +17,8 @@ int main () return 0; } -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } } +// We read a pending for both '::frob' and '::frob::store'. +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::frob'} module } } // { dg-final { scan-lang-dump-not {Reading definition function_decl '::frob@TPL:.::store@TPL:.<int>'} module } } // { dg-final { scan-assembler-not {_ZN4frob5storeIiEEvT_:} } } diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C index 97aa251d3e0..ea8d014e88b 100644 --- a/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C @@ -14,4 +14,4 @@ int main () return 0; } -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } } +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::X'} module } } diff --git a/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C b/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C index ff3d84c1384..300f649ac27 100644 --- a/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C +++ b/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C @@ -14,4 +14,4 @@ int main () return 0; } -// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } } +// { dg-final { scan-lang-dump {Reading 2 pending entities keyed to '::X'} module } }
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 -- In some cases we can access members of a namespace-scope class without ever having performed name-lookup on it; this can occur when a forward-declaration of the class is used as a return type, for instance, or with PIMPL. One possible approach would be to do name lookup in complete_type to force lazy loading to occur, but this seems overly expensive for a relatively rare case. Instead, this patch generalises the existing pending-entity support to handle this case as well. Unfortunately this does mean that almost every class definition will be added to the pending-entity table, and almost always unnecessarily, but I don't see a good way to avoid this. gcc/cp/ChangeLog: * module.cc (depset::DB_IS_MEMBER_BIT): Rename to... (depset::DB_IS_PENDING_BIT): ...this. (depset::is_member): Remove. (depset::is_pending_entity): New function. (depset::hash::make_dependency): Mark definitions of namespace-scope types as maybe-pending entities. (depset::hash::add_class_entities): Rename DB_IS_MEMBER_BIT to DB_IS_PENDING_BIT. (depset::hash::find_dependencies): Use is_pending_entity instead of is_member. (module_state::write_pendings): Likewise; adjust comment. gcc/testsuite/ChangeLog: * g++.dg/modules/inst-4_b.C: Adjust pending-entity count. * g++.dg/modules/member-def-1_c.C: Likewise. * g++.dg/modules/member-def-2_c.C: Likewise. * g++.dg/modules/tpl-spec-3_b.C: Likewise. * g++.dg/modules/tpl-spec-4_b.C: Likewise. * g++.dg/modules/tpl-spec-5_b.C: Likewise. * g++.dg/modules/class-9_a.H: New test. * g++.dg/modules/class-9_b.H: New test. * g++.dg/modules/class-9_c.C: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> --- gcc/cp/module.cc | 54 +++++++++++-------- gcc/testsuite/g++.dg/modules/class-9_a.H | 8 +++ gcc/testsuite/g++.dg/modules/class-9_b.H | 7 +++ gcc/testsuite/g++.dg/modules/class-9_c.C | 10 ++++ gcc/testsuite/g++.dg/modules/inst-4_b.C | 2 +- gcc/testsuite/g++.dg/modules/member-def-1_c.C | 4 +- gcc/testsuite/g++.dg/modules/member-def-2_c.C | 2 +- gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C | 3 +- gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C | 2 +- gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C | 2 +- 10 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/class-9_a.H create mode 100644 gcc/testsuite/g++.dg/modules/class-9_b.H create mode 100644 gcc/testsuite/g++.dg/modules/class-9_c.C