Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 --
This finishes the reworking of ADL handling for modules for PR117658. [basic.link] p18 says that we should diagnose any references to a TU-local entity from a different TU; with our fixed handling of ADL this is now possible to occur. To do this we need to generate fake bindings for these decls on stream-out so that importers know that we have a visible name in this namespace. We don't actually need (or want) to provide full DECLs though, as that could potentially involve streaming other TU-local entities, so we just build a TU_LOCAL_ENTITY for the decl on receipt. The patch also uses 'decl_as_string' to give a more descriptive name for these decls when erroring. We also need somewhere to store these decls. We only actually need the decls for diagnostics; for correctness we only need to know whether any such decls existed, so to not mess with the existing packing of bindings or usages of the OVERLOADs this patch adds a new map to the binding vector that can be looked up when diagnostics need to be generated. Finally, as specified this diagnostic is a pretty broad hammer, as any internal-linkage purview function will stop ADL in exported templates from working with that name. So this patch just makes it a pedwarn and provides an option to disable if needed. PR c++/117658 gcc/c-family/ChangeLog: * c.opt: New flag '-Wexternal-tu-local'. * c.opt.urls: Regenerate. gcc/cp/ChangeLog: * cp-tree.h (TU_LOCAL_ENTITY_NAME): Clarify meaning. * module.cc (depset::entity_kind): New enumerator, assert that we have enough bits reserved. (depset::disc_bits): Assert the discriminator has enough bits. (depset::entity_kind_name): Add 'tu-local' case, assert we have an entry for all relevant entry_kinds. (name_for_tu_local_decl): New function. (trees_out::tree_node): Use it. (depset::hash::make_dependency): Validate EK_TU_LOCAL. (depset::hash::add_binding_entity): Generate bindings for internal purview functions. (enum ct_bind_flags): New enum for TU-local decls. (depset::hash::find_dependencies): Handle EK_TU_LOCAL entities. (binding_cmp): Likewise. (sort_cluster): Likewise. (module_state::write_cluster): Likewise. (module_state::read_cluster): Likewise. * name-lookup.cc (append_imported_binding_slot): Propagate internal decl list when growing binding vector. (name_lookup::adl_namespace_fns): Diagnose if naming a TU-local entity from a different TU. (set_module_binding): Include any internal decls in binding. * name-lookup.h (struct module_tree_map_traits): New type. (struct tree_binding_vec): Add member 'internal_decls'. (BINDING_VECTOR_INTERNAL_DECLS): New getter. (MODULE_BINDING_INTERNAL_DECLS_P): New flag. (set_module_binding): Add parameter. gcc/doc/ChangeLog: * invoke.texi: Document '-Wno-external-tu-local'. gcc/testsuite/ChangeLog: * g++.dg/modules/adl-6_c.C: Adjust diagnostics. * g++.dg/modules/internal-14_c.C: Likewise. * g++.dg/modules/internal-15_a.C: New test. * g++.dg/modules/internal-15_b.C: New test. Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> --- gcc/c-family/c.opt | 4 + gcc/c-family/c.opt.urls | 3 + gcc/cp/cp-tree.h | 3 +- gcc/cp/module.cc | 112 ++++++++++++++++--- gcc/cp/name-lookup.cc | 58 ++++++++-- gcc/cp/name-lookup.h | 16 ++- gcc/doc/invoke.texi | 28 ++++- gcc/testsuite/g++.dg/modules/adl-6_c.C | 5 +- gcc/testsuite/g++.dg/modules/internal-14_c.C | 2 +- gcc/testsuite/g++.dg/modules/internal-15_a.C | 28 +++++ gcc/testsuite/g++.dg/modules/internal-15_b.C | 13 +++ 11 files changed, 241 insertions(+), 31 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/internal-15_a.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-15_b.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 3f5e2f0874d..9e0e898a356 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -761,6 +761,10 @@ Wexpansion-to-defined C ObjC C++ ObjC++ CPP(warn_expansion_to_defined) CppReason(CPP_W_EXPANSION_TO_DEFINED) Var(cpp_warn_expansion_to_defined) Init(0) Warning EnabledBy(Wextra || Wpedantic) Warn if \"defined\" is used outside #if. +Wexternal-tu-local +C++ ObjC++ Var(warn_tu_local) Warning Init(1) +Warn about naming a TU-local entity declared in another translation unit. + Wextra C ObjC C++ ObjC++ Warning ; in common.opt diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls index e09d51d8afb..1d3f27af228 100644 --- a/gcc/c-family/c.opt.urls +++ b/gcc/c-family/c.opt.urls @@ -376,6 +376,9 @@ UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexceptions) Wexpansion-to-defined UrlSuffix(gcc/Warning-Options.html#index-Wexpansion-to-defined) +Wexternal-tu-local +UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexternal-tu-local) + Wextra UrlSuffix(gcc/Warning-Options.html#index-Wextra) LangUrlSuffix_D(gdc/Warnings.html#index-Wextra) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Wextra) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d531bcd833b..ed46203662b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1856,7 +1856,8 @@ struct GTY(()) tree_tu_local_entity { location_t loc; }; -/* The name of a translation-unit-local entity. */ +/* The human-readable name of a translation-unit-local entity as + an IDENTIFIER_NODE. */ #define TU_LOCAL_ENTITY_NAME(NODE) \ (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index d5c3a83c728..5bc19a5e3f7 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2341,6 +2341,7 @@ public: EK_PARTIAL, /* A partial specialization. */ EK_USING, /* A using declaration (at namespace scope). */ EK_NAMESPACE, /* A namespace. */ + EK_TU_LOCAL, /* A TU-local decl for ADL. */ EK_REDIRECT, /* Redirect to a template_decl. */ EK_EXPLICIT_HWM, EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded. */ @@ -2351,6 +2352,8 @@ public: EK_BITS = 3 /* Only need to encode below EK_EXPLICIT_HWM. */ }; + static_assert (EK_EXPLICIT_HWM < (1u << EK_BITS), + "not enough bits reserved for entity_kind"); private: /* Placement of bit fields in discriminator. */ @@ -2375,7 +2378,10 @@ private: awkward. */ DB_TYPE_SPEC_BIT, /* Specialization in the type table. */ DB_FRIEND_SPEC_BIT, /* An instantiated template friend. */ + DB_HWM, }; + static_assert (DB_HWM <= sizeof(discriminator) * CHAR_BIT, + "not enough bits in discriminator"); public: /* The first slot is special for EK_SPECIALIZATIONS it is a @@ -2700,7 +2706,9 @@ depset::entity_kind_name () const /* Same order as entity_kind. */ static const char *const names[] = {"decl", "specialization", "partial", "using", - "namespace", "redirect", "binding"}; + "namespace", "tu-local", "redirect", "binding"}; + static_assert (ARRAY_SIZE (names) == EK_EXPLICIT_HWM + 1, + "names must have an entry for every explicit entity_kind"); entity_kind kind = get_entity_kind (); gcc_checking_assert (kind < ARRAY_SIZE (names)); return names[kind]; @@ -9980,6 +9988,16 @@ trees_out::find_tu_local_decl (tree t) return cp_walk_tree_without_duplicates (&t, walker, this); } +/* Get the name for TU-local decl T to be used in diagnostics. */ + +static tree +name_for_tu_local_decl (tree t) +{ + int flags = (TFF_SCOPE | TFF_DECL_SPECIFIERS); + const char *str = decl_as_string (t, flags); + return get_identifier (str); +} + /* Stream out tree node T. We automatically create local back references, which is essentially a single pass lisp self-referential structure pretty-printer. */ @@ -10024,8 +10042,7 @@ trees_out::tree_node (tree t) && dump ("Writing TU-local entity:%d %C:%N", tag, TREE_CODE (t), t); } - /* TODO: Get a more descriptive name? */ - tree_node (DECL_NAME (local_decl)); + tree_node (name_for_tu_local_decl (local_decl)); if (state) state->write_location (*this, DECL_SOURCE_LOCATION (local_decl)); goto done; @@ -14187,6 +14204,8 @@ depset::hash::make_dependency (tree decl, entity_kind ek) gcc_checking_assert (!is_key_order ()); if (ek == EK_USING) gcc_checking_assert (TREE_CODE (decl) == OVERLOAD); + if (ek == EK_TU_LOCAL) + gcc_checking_assert (DECL_DECLARES_FUNCTION_P (decl)); if (TREE_CODE (decl) == TEMPLATE_DECL) /* The template should have copied these from its result decl. */ @@ -14387,7 +14406,8 @@ depset::hash::make_dependency (tree decl, entity_kind ek) dump (dumper::DEPEND) && dump ("%s on %s %C:%N found", ek == EK_REDIRECT ? "Redirect" - : for_binding ? "Binding" : "Dependency", + : (for_binding || ek == EK_TU_LOCAL) ? "Binding" + : "Dependency", dep->entity_kind_name (), TREE_CODE (decl), decl); return dep; @@ -14558,9 +14578,21 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_) than trying to clear out bindings after the fact. */ return false; + bool internal_decl = false; if (!header_module_p () && data->hash->is_tu_local_entity (decl)) - /* Ignore TU-local entitites. */ - return false; + { + /* A TU-local entity. For ADL we still need to create bindings + for internal-linkage functions attached to a named module. */ + if (DECL_DECLARES_FUNCTION_P (inner) + && DECL_LANG_SPECIFIC (inner) + && DECL_MODULE_ATTACH_P (inner)) + { + gcc_checking_assert (!DECL_MODULE_EXPORT_P (inner)); + internal_decl = true; + } + else + return false; + } if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == TYPE_DECL) @@ -14655,8 +14687,13 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_) OVL_EXPORT_P (decl) = true; } - depset *dep = data->hash->make_dependency - (decl, flags & WMB_Using ? EK_USING : EK_FOR_BINDING); + entity_kind ek = EK_FOR_BINDING; + if (internal_decl) + ek = EK_TU_LOCAL; + else if (flags & WMB_Using) + ek = EK_USING; + + depset *dep = data->hash->make_dependency (decl, ek); if (flags & WMB_Hidden) dep->set_hidden_binding (); data->binding->deps.safe_push (dep); @@ -15107,9 +15144,12 @@ depset::hash::find_dependencies (module_state *module) walker.begin (); if (current->get_entity_kind () == EK_USING) walker.tree_node (OVL_FUNCTION (decl)); + else if (current->get_entity_kind () == EK_TU_LOCAL) + /* We only stream its name and location. */ + module->note_location (DECL_SOURCE_LOCATION (decl)); else if (TREE_VISITED (decl)) /* A global tree. */; - else if (item->get_entity_kind () == EK_NAMESPACE) + else if (current->get_entity_kind () == EK_NAMESPACE) { module->note_location (DECL_SOURCE_LOCATION (decl)); add_namespace_context (current, CP_DECL_CONTEXT (decl)); @@ -15224,6 +15264,12 @@ binding_cmp (const void *a_, const void *b_) return a_implicit ? -1 : +1; /* Implicit first. */ } + /* TU-local before non-TU-local. */ + bool a_internal = a->get_entity_kind () == depset::EK_TU_LOCAL; + bool b_internal = b->get_entity_kind () == depset::EK_TU_LOCAL; + if (a_internal != b_internal) + return a_internal ? -1 : +1; /* Internal first. */ + /* Hidden before non-hidden. */ bool a_hidden = a->is_hidden (); bool b_hidden = b->is_hidden (); @@ -15558,12 +15604,15 @@ sort_cluster (depset::hash *original, depset *scc[], unsigned size) use_lwm--; break; } - /* We must have copied a using, so move it too. */ + /* We must have copied a using or TU-local, so move it too. */ dep = scc[ix]; - gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING); + gcc_checking_assert + (dep->get_entity_kind () == depset::EK_USING + || dep->get_entity_kind () == depset::EK_TU_LOCAL); /* FALLTHROUGH */ case depset::EK_USING: + case depset::EK_TU_LOCAL: if (--use_lwm != ix) { scc[ix] = scc[use_lwm]; @@ -16563,6 +16612,7 @@ enum ct_bind_flags cbf_export = 0x1, /* An exported decl. */ cbf_hidden = 0x2, /* A hidden (friend) decl. */ cbf_using = 0x4, /* A using decl. */ + cbf_internal = 0x8, /* A TU-local decl. */ }; /* DEP belongs to a different cluster, seed it to prevent @@ -16634,7 +16684,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, gcc_checking_assert (dep->is_import () || TREE_VISITED (dep->get_entity ()) || (dep->get_entity_kind () - == depset::EK_USING)); + == depset::EK_USING) + || (dep->get_entity_kind () + == depset::EK_TU_LOCAL)); } } break; @@ -16647,6 +16699,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, /* FALLTHROUGH */ case depset::EK_USING: + case depset::EK_TU_LOCAL: gcc_checking_assert (!b->is_import () && !b->is_unreached ()); dump (dumper::CLUSTER) @@ -16719,7 +16772,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, depset *dep = b->deps[jx]; tree bound = dep->get_entity (); unsigned flags = 0; - if (dep->get_entity_kind () == depset::EK_USING) + if (dep->get_entity_kind () == depset::EK_TU_LOCAL) + flags |= cbf_internal; + else if (dep->get_entity_kind () == depset::EK_USING) { tree ovl = bound; bound = OVL_FUNCTION (bound); @@ -16745,7 +16800,13 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, gcc_checking_assert (DECL_P (bound)); sec.i (flags); - sec.tree_node (bound); + if (flags & cbf_internal) + { + sec.tree_node (name_for_tu_local_decl (bound)); + write_location (sec, DECL_SOURCE_LOCATION (bound)); + } + else + sec.tree_node (bound); } /* Terminate the list. */ @@ -16754,6 +16815,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, break; case depset::EK_USING: + case depset::EK_TU_LOCAL: dump () && dump ("Depset:%u %s %C:%N", ix, b->entity_kind_name (), TREE_CODE (decl), decl); break; @@ -16871,6 +16933,7 @@ module_state::read_cluster (unsigned snum) tree name = sec.tree_node (); tree decls = NULL_TREE; tree visible = NULL_TREE; + tree internal = NULL_TREE; tree type = NULL_TREE; bool dedup = false; bool global_p = is_header (); @@ -16886,6 +16949,23 @@ module_state::read_cluster (unsigned snum) if ((flags & cbf_hidden) && (flags & (cbf_using | cbf_export))) sec.set_overrun (); + if ((flags & cbf_internal) + && flags != cbf_internal) + sec.set_overrun (); + + if (flags & cbf_internal) + { + tree name = sec.tree_node (); + location_t loc = read_location (sec); + if (sec.get_overrun ()) + break; + + tree decl = make_node (TU_LOCAL_ENTITY); + TU_LOCAL_ENTITY_NAME (decl) = name; + TU_LOCAL_ENTITY_LOCATION (decl) = loc; + internal = tree_cons (NULL_TREE, decl, internal); + continue; + } tree decl = sec.tree_node (); if (sec.get_overrun ()) @@ -16980,7 +17060,7 @@ module_state::read_cluster (unsigned snum) } } - if (!decls) + if (!decls && !internal) sec.set_overrun (); if (sec.get_overrun ()) @@ -16989,7 +17069,7 @@ module_state::read_cluster (unsigned snum) dump () && dump ("Binding of %P", ns, name); if (!set_module_binding (ns, name, mod, global_p, is_module () || is_partition (), - decls, type, visible)) + decls, type, visible, internal)) sec.set_overrun (); } break; diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 32af7a6aed9..8d7fc06f698 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -353,6 +353,8 @@ append_imported_binding_slot (tree *slot, tree name, unsigned ix) tree new_vec = make_binding_vec (name, want); BINDING_VECTOR_NUM_CLUSTERS (new_vec) = have + 1; + BINDING_VECTOR_INTERNAL_DECLS (new_vec) + = BINDING_VECTOR_INTERNAL_DECLS (*slot); BINDING_VECTOR_GLOBAL_DUPS_P (new_vec) = BINDING_VECTOR_GLOBAL_DUPS_P (*slot); BINDING_VECTOR_PARTITION_DUPS_P (new_vec) @@ -1304,10 +1306,34 @@ name_lookup::adl_namespace_fns (tree scope, bitmap imports, } /* For lookups on the instantiation path we can see any - module-linkage declaration; otherwise we should only - see exported decls. */ + declarations visible at any point on the path; + otherwise we should only see exported decls. */ if (on_inst_path) - bind = STAT_DECL (bind); + { + /* If there are any internal functions visible, naming + them outside that module is ill-formed. */ + auto_diagnostic_group d; + if (MODULE_BINDING_INTERNAL_DECLS_P (bind) + && pedwarn (input_location, OPT_Wexternal_tu_local, + "overload set for argument-dependent " + "lookup of %<%D::%D%> in module %qs " + "contains TU-local entities", + scope, name, module_name (mod, false))) + { + tree *tu_locals + = BINDING_VECTOR_INTERNAL_DECLS (val)->get (mod); + gcc_checking_assert (tu_locals && *tu_locals); + for (tree t = *tu_locals; t; t = TREE_CHAIN (t)) + { + tree decl = TREE_VALUE (t); + inform (TU_LOCAL_ENTITY_LOCATION (decl), + "ignoring %qD declared here " + "with internal linkage", + TU_LOCAL_ENTITY_NAME (decl)); + } + } + bind = STAT_DECL (bind); + } else bind = STAT_VISIBLE (bind); } @@ -4427,18 +4453,21 @@ import_module_binding (tree ns, tree name, unsigned mod, unsigned snum) /* An import of MODULE is binding NS::NAME. There should be no existing binding for >= MODULE. GLOBAL_P indicates whether the bindings include global module entities. PARTITION_P is true if - it is part of the current module. VALUE and TYPE are the value - and type bindings. VISIBLE are the value bindings being exported. */ + it is part of the current module. VALUE and TYPE are the value + and type bindings. VISIBLE are the value bindings being exported. + INTERNAL is a TREE_LIST of any TU-local names visible for ADL. */ bool set_module_binding (tree ns, tree name, unsigned mod, bool global_p, - bool partition_p, tree value, tree type, tree visible) + bool partition_p, tree value, tree type, tree visible, + tree internal) { - if (!value) + if (!value && !internal) /* Bogus BMIs could give rise to nothing to bind. */ return false; - gcc_assert (TREE_CODE (value) != NAMESPACE_DECL + gcc_assert (!value + || TREE_CODE (value) != NAMESPACE_DECL || DECL_NAMESPACE_ALIAS (value)); gcc_checking_assert (mod); @@ -4450,7 +4479,7 @@ set_module_binding (tree ns, tree name, unsigned mod, bool global_p, return false; tree bind = value; - if (type || visible != bind || partition_p || global_p) + if (type || visible != bind || internal || partition_p || global_p) { bind = stat_hack (bind, type); STAT_VISIBLE (bind) = visible; @@ -4459,6 +4488,17 @@ set_module_binding (tree ns, tree name, unsigned mod, bool global_p, STAT_TYPE_VISIBLE_P (bind) = true; } + /* If this has internal declarations, track them for diagnostics. */ + if (internal) + { + if (!BINDING_VECTOR_INTERNAL_DECLS (*slot)) + BINDING_VECTOR_INTERNAL_DECLS (*slot) + = module_tree_map_t::create_ggc (); + bool existed = BINDING_VECTOR_INTERNAL_DECLS (*slot)->put (mod, internal); + gcc_checking_assert (!existed); + MODULE_BINDING_INTERNAL_DECLS_P (bind) = true; + } + /* Note if this is this-module and/or global binding. */ if (partition_p) MODULE_BINDING_PARTITION_P (bind) = true; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 5b142e74899..3815b8c1c96 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -142,15 +142,23 @@ struct GTY(()) binding_cluster #define BINDING_VECTOR_CLUSTER(NODE,IX) \ (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->vec[IX]) +struct module_tree_map_traits + : simple_hashmap_traits<int_hash<unsigned, 0>, tree> {}; +typedef hash_map<unsigned, tree, module_tree_map_traits> module_tree_map_t; + struct GTY(()) tree_binding_vec { struct tree_base base; tree name; + module_tree_map_t *internal_decls; binding_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1]; }; /* The name of a module vector. */ #define BINDING_VECTOR_NAME(NODE) \ (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->name) +/* A collection of internal functions for ADL in this binding. */ +#define BINDING_VECTOR_INTERNAL_DECLS(NODE) \ + (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->internal_decls) /* tree_binding_vec does uses base.u.dependence_info.base field for length. It does not have lang_flag etc available! */ @@ -166,6 +174,11 @@ struct GTY(()) tree_binding_vec { #define BINDING_VECTOR_PARTITION_DUPS_P(NODE) \ (BINDING_VECTOR_CHECK (NODE)->base.volatile_flag) +/* True if the binding slot has internal-linkage functions that should + cause ADL to error. */ +#define MODULE_BINDING_INTERNAL_DECLS_P(NODE) \ + (OVERLOAD_CHECK (NODE)->base.private_flag) + /* These two flags indicate the provenence of the bindings on this particular vector slot. We can of course determine this from slot number, but that's a relatively expensive lookup. This avoids @@ -491,7 +504,8 @@ extern bool import_module_binding (tree ctx, tree name, unsigned mod, unsigned snum); extern bool set_module_binding (tree ctx, tree name, unsigned mod, bool global_p, bool partition_p, - tree value, tree type, tree visible); + tree value, tree type, tree visible, + tree internal); extern void add_module_namespace_decl (tree ns, tree decl); enum WMB_Flags diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d0c13d4a24e..2a58a387abd 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -272,7 +272,7 @@ in the following sections. -Woverloaded-virtual -Wno-pmf-conversions -Wself-move -Wsign-promo -Wsized-deallocation -Wsuggest-final-methods -Wsuggest-final-types -Wsuggest-override -Wno-template-body --Wno-template-id-cdtor -Wtemplate-names-tu-local +-Wno-template-id-cdtor -Wtemplate-names-tu-local -Wno-external-tu-local -Wno-terminate -Wno-vexing-parse -Wvirtual-inheritance -Wno-virtual-move-assign -Wvolatile} @@ -4756,6 +4756,32 @@ The presence of an explicit instantiation silences the warning. This flag is enabled by @option{-Wextra}. +@opindex Wexternal-tu-local +@opindex Wno-external-tu-local +@item -Wno-external-tu-local +Warn when naming a TU-local entity outside of the translation unit it +was declared in. Such declarations will be ignored during name lookup. +This can occur when performing ADL from a template declared in the same +TU as the internal function: + +@smallexample +export module M; +template <typename T> void foo(T t) @{ + bar(t); +@} +struct S @{@} s; +static void bar(S) @{@} // internal linkage + +// instantiating foo(s) from outside this TU can see ::bar, +// but naming it there is ill-formed. +@end smallexample + +This can be worked around by making @code{bar} attached to the global +module, using @code{extern "C++"}. + +This warning is enabled by default, and is upgraded to an error by +@option{-pedantic-errors}. + @opindex Wterminate @opindex Wno-terminate @item -Wno-terminate @r{(C++ and Objective-C++ only)} diff --git a/gcc/testsuite/g++.dg/modules/adl-6_c.C b/gcc/testsuite/g++.dg/modules/adl-6_c.C index 99b6c4c043e..2c675f5241a 100644 --- a/gcc/testsuite/g++.dg/modules/adl-6_c.C +++ b/gcc/testsuite/g++.dg/modules/adl-6_c.C @@ -1,5 +1,5 @@ // PR c++/117658 -// { dg-additional-options "-fmodules" } +// { dg-additional-options "-fmodules -Wno-error=external-tu-local" } import N; @@ -22,7 +22,8 @@ void test() { apply_err(x); // error: R::g has internal linkage and cannot be used outside N // { dg-message "here" "" { target *-*-* } .-1 } - // { dg-error "'g'" "" { target *-*-* } 0 } + // { dg-warning "lookup of 'R::g'" "" { target *-*-* } 0 } + // { dg-error "'g' was not declared" "" { target *-*-* } 0 } auto y = make_Y(); f(y); // OK, I::B::f and I::A::Y have matching innermost non-inline namespace diff --git a/gcc/testsuite/g++.dg/modules/internal-14_c.C b/gcc/testsuite/g++.dg/modules/internal-14_c.C index 4f8e785ce87..50fb8e6f1a7 100644 --- a/gcc/testsuite/g++.dg/modules/internal-14_c.C +++ b/gcc/testsuite/g++.dg/modules/internal-14_c.C @@ -4,6 +4,6 @@ import m; int main() { - // { dg-error "instantiation exposes TU-local entity '(fun1|Dodgy)'" "" { target *-*-* } 0 } + // { dg-error "instantiation exposes TU-local entity '\[^']*(fun1|Dodgy)\[^']*'" "" { target *-*-* } 0 } fun2(123); // { dg-message "required from here" } } diff --git a/gcc/testsuite/g++.dg/modules/internal-15_a.C b/gcc/testsuite/g++.dg/modules/internal-15_a.C new file mode 100644 index 00000000000..03fec2a4541 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-15_a.C @@ -0,0 +1,28 @@ +// { dg-additional-options "-fmodules -fdump-lang-module-graph -Wno-global-module" } +// { dg-module-cmi A } + +export module A; + +namespace N { + struct A {}; + void adl(A); + inline namespace inner { + static void adl(int); + } +} +namespace G { + struct B {}; + void adl(B); + namespace { + extern "C++" void adl(int); + } +} +void adl(double); + +template <typename T> +inline void h(T t) { + adl(t); +} + +// { dg-final { scan-lang-dump {Binding on tu-local function_decl:'::N::inner::adl' found} module } } +// { dg-final { scan-lang-dump-not {'G::_GLOBAL__N_1::adl'} module } } diff --git a/gcc/testsuite/g++.dg/modules/internal-15_b.C b/gcc/testsuite/g++.dg/modules/internal-15_b.C new file mode 100644 index 00000000000..003d948b014 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-15_b.C @@ -0,0 +1,13 @@ +// { dg-additional-options "-fmodules -pedantic-errors" } + +module A; + +void other() { + adl(N::A{}); // OK, lookup occurs from here + h(0); // OK, doesn't consider N::inner::adl + + h(N::A{}); // { dg-message "required from here" } + // { dg-error "TU-local" "" { target *-*-* } 0 } + + h(G::B{}); // OK, G::adl is not attached to A +} -- 2.51.0