diff options
author | Nathaniel Shead <nathanieloshead@gmail.com> | 2024-10-21 22:55:46 +1100 |
---|---|---|
committer | Nathaniel Shead <nathanieloshead@gmail.com> | 2024-10-22 22:34:30 +1100 |
commit | 9f9afc65bb7823db84ceeb7981965916c4a583db (patch) | |
tree | 7e9422f47cf7ccffc21f724f8060335e5f5bd51c /gcc/cp | |
parent | d464a52d0678dfea523a60efe8b792ba1b8d40db (diff) | |
download | gcc-9f9afc65bb7823db84ceeb7981965916c4a583db.zip gcc-9f9afc65bb7823db84ceeb7981965916c4a583db.tar.gz gcc-9f9afc65bb7823db84ceeb7981965916c4a583db.tar.bz2 |
c++/modules: Handle forward-declared class types
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>
Reviewed-by: Jason Merrill <jason@redhat.com>
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/module.cc | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 2dc59ce..fd9b1d3 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; |