On Mon, Jan 27, 2025 at 10:20:05AM -0500, Patrick Palka wrote: > On Sat, 12 Oct 2024, Nathaniel Shead wrote: > > > This version rewords all "ignored exposures" language. > > > > I haven't fixed up the issue with DECL_TEMPLATE_INSTANTIATIONS for this > > patch. I'll try to get to that as a separate patch if I find the time, > > but it's not 100% needed here I don't think. > > > > It seems the discussion around PR115126 RE: the libgcc changes that > > people are leaning towards reverting the changes I made to the gthread > > static variables and instead having some sort of attribute to instruct > > the compiler to ignore the ODR issues; that should be relatively > > straight-forward to add (maybe something like [[gnu::ignore_tu_local]]?) > > but I'll probably handle that as a separate patch once I'm sure that's > > what we actually want to do, and would probably be worth corresponding > > with the Clang folks to see what thoughts they have. > > > > Finally, I wasn't able to work out a good way to fold non-ODR usages of > > TU-local constants early. I attempted modifying 'mark_use', which > > helped for 'constexpr' usages (though I ran into issues with e.g. > > > > constexpr int n[1] = 0; > > constexpr int x = n ? 1 : 2; > > > > segfaulting as build_address was not expecting to see a constructor > > rather than a declaration here). But I didn't look too hard into > > solving this as it appears that any modifications made by 'mark_use' are > > not actually applied to the primary template at all, and this is > > consistent across many places I investigated. > > > > Given that erroring on these cases is still the status-quo, how easy it > > is to workaround most of the time, and that I'm still not sure how to > > solve this, I've also left this as a FIXME (with an XFAILed testcase) to > > revisit later. > > > > OK for trunk? > > > > -- >8 -- > > > > [basic.link] p14 lists a number of circumstances where a declaration > > naming a TU-local entity is not an exposure, notably the bodies of > > non-inline templates and friend declarations in classes. This patch > > ensures that these references do not error when exporting the module. > > > > We do need to still error on instantiation from a different module, > > however, in case this refers to a TU-local entity. As such this patch > > adds a new tree TU_LOCAL_ENTITY which is used purely as a placeholder to > > poison any attempted template instantiations that refer to it. > > I'm a bit late to the party, but this is a nice patch series! I have a > couple of comments below. > > > > > This is also streamed for friend decls so that merging (based on the > > index of an entity into the friend decl list) doesn't break and to > > prevent complicating the logic; I imagine this shouldn't ever come up > > though. > > > > We also add a new warning, '-Wtemplate-names-tu-local', to handle the > > case where someone accidentally refers to a TU-local value from within a > > non-inline function template. This will compile without errors as-is, > > but any attempt to instantiate the decl will fail; this warning can be > > used to ensure that this doesn't happen. Unfortunately the warning has > > quite some false positives; for instance, a user could deliberately only > > call explicit instantiations of the decl, or use 'if constexpr' to avoid > > instantiating the TU-local entity from other TUs, neither of which are > > currently detected. > > > > The main piece that this patch doesn't yet attempt to solve is ADL: as > > specified, if ADL adds an overload set that includes a translation-unit > > local entity when instantiating a template, that overload set is now > > poisoned and counts as an exposure. Unfortunately, we don't currently > > differentiate between decls that are hidden due to not being exported, > > or decls that are hidden due to being hidden friends, so this patch > > instead just keeps the current (wrong) behaviour of non-exported > > entities not being visible to ADL at all. > > > > Additionally, this patch doesn't attempt to ignore non-ODR uses of > > constants in constexpr functions or templates. The obvious approach of > > folding them early in 'mark_use' doesn't seem to work (for a variety of > > reasons), so this leaves this to a later patch to implement, as it's at > > least no worse than the current behaviour and easy enough to workaround. > > > > For completeness this patch adds a new xtreme-header testcase to ensure > > that we have no regressions with regards to exposures of TU-local > > declarations in the standard library header files. A more restrictive > > test would be to do 'export extern "C++"' here, but unfortunately the > > system headers on some targets declare TU-local entities, so we'll make > > do with checking that at least the C++ standard library headers don't > > refer to such entities. > > > > gcc/c-family/ChangeLog: > > > > * c.opt: New warning '-Wtemplate-names-tu-local'. > > > > gcc/cp/ChangeLog: > > > > * cp-objcp-common.cc (cp_tree_size): Add TU_LOCAL_ENTITY. > > * cp-tree.def (TU_LOCAL_ENTITY): New tree code. > > * cp-tree.h (struct tree_tu_local_entity): New type. > > (TU_LOCAL_ENTITY_NAME): New accessor. > > (TU_LOCAL_ENTITY_LOCATION): New accessor. > > (enum cp_tree_node_structure_enum): Add TS_CP_TU_LOCAL_ENTITY. > > (union GTY): Add tu_local_entity field. > > * module.cc (enum tree_tag): New flag DB_REFS_TU_LOCAL_BIT. > > (depset::has_defn): Override for TU-local entities. > > (depset::refs_tu_local): New accessor. > > (depset::hash::ignore_tu_local): New field. > > (depset::hash::hash): Initialize it. > > (trees_out::tree_tag::tt_tu_local): New flag. > > (trees_out::writing_local_entities): New field. > > (trees_out::is_initial_scan): New function. > > (trees_out::tu_local_count): New counter. > > (trees_out::trees_out): Initialize writing_local_entities. > > (dumper::impl::nested_name): Handle TU_LOCAL_ENTITY. > > (trees_out::instrument): Report TU-local entity counts. > > (trees_out::decl_value): Early exit for TU-local entities. > > (trees_in::decl_value): Handle typedefs of TU-local entities. > > (trees_out::decl_node): Adjust assertion to cope with early exit > > of TU-local deps. Always write TU-local entities by value. > > (trees_out::type_node): Handle TU-local types. > > (trees_out::has_tu_local_dep): New function. > > (trees_out::find_tu_local_decl): New function. > > (trees_out::tree_node): Intercept TU-local entities and write > > placeholder values for them instead of normal streaming. > > (trees_in::tree_node): Handle TU-local entities and TU-local > > template results. > > (trees_out::write_function_def): Ignore exposures in non-inline > > function bodies. > > (trees_out::write_var_def): Ignore exposures in initializers. > > (trees_out::write_class_def): Ignore exposures in friend decls. > > (trees_in::read_class_def): Skip TU-local friends. > > (trees_out::write_definition): Record whether we're writing a > > decl which refers to TU-local entities. > > (depset::hash::add_dependency): Only mark as exposure if we're not > > ignoring TU-local entities. > > (depset::hash::find_dependencies): Use depset's own is_key_order > > function rather than delegating via walker. Pass whether the > > decl has ignored TU-local entities in its definition. > > (depset::hash::finalize_dependencies): Implement new warning > > Wtemplate-names-tu-local. > > (module_state::intercluster_seed): Don't seed TU-local deps. > > (module_state::write_cluster): Pass whether the decl has ignored > > TU-local entities in its definition. > > * pt.cc (complain_about_tu_local_entity): New function. > > (expr_contains_tu_local_entity): New function. > > (function_contains_tu_local_entity): New function. > > (instantiate_class_template): Skip TU-local friends. > > (tsubst_decl): Handle typedefs of TU-local entities. > > (tsubst): Complain about TU-local entities. > > (dependent_operand_p): Early exit for TU-local entities so we > > don't attempt to constant-evaluate them. > > (tsubst_expr): Detect and complain about TU-local entities. > > > > gcc/ChangeLog: > > > > * doc/invoke.texi: Document -Wtemplate-names-tu-local. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/modules/internal-5_a.C: New test. > > * g++.dg/modules/internal-5_b.C: New test. > > * g++.dg/modules/internal-6.C: New test. > > * g++.dg/modules/internal-7_a.C: New test. > > * g++.dg/modules/internal-7_b.C: New test. > > * g++.dg/modules/internal-8_a.C: New test. > > * g++.dg/modules/xtreme-header-8.C: New test. > > > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> > > Reviewed-by: Jason Merrill <ja...@redhat.com> > > --- > > gcc/c-family/c.opt | 4 + > > gcc/cp/cp-objcp-common.cc | 1 + > > gcc/cp/cp-tree.def | 6 + > > gcc/cp/cp-tree.h | 21 +- > > gcc/cp/module.cc | 318 +++++++++++++++--- > > gcc/cp/pt.cc | 98 +++++- > > gcc/doc/invoke.texi | 19 +- > > gcc/testsuite/g++.dg/modules/internal-5_a.C | 110 ++++++ > > gcc/testsuite/g++.dg/modules/internal-5_b.C | 30 ++ > > gcc/testsuite/g++.dg/modules/internal-6.C | 24 ++ > > gcc/testsuite/g++.dg/modules/internal-7_a.C | 75 +++++ > > gcc/testsuite/g++.dg/modules/internal-7_b.C | 21 ++ > > gcc/testsuite/g++.dg/modules/internal-8_a.C | 35 ++ > > .../g++.dg/modules/xtreme-header-8.C | 8 + > > 14 files changed, 721 insertions(+), 49 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_b.C > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-6.C > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_b.C > > create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_a.C > > create mode 100644 gcc/testsuite/g++.dg/modules/xtreme-header-8.C > > > > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > > index 9d1fccadbf9..6e5ddefd0e2 100644 > > --- a/gcc/c-family/c.opt > > +++ b/gcc/c-family/c.opt > > @@ -1450,6 +1450,10 @@ Wtemplate-id-cdtor > > C++ ObjC++ Var(warn_template_id_cdtor) Warning > > Warn about simple-template-id in a constructor or destructor. > > > > +Wtemplate-names-tu-local > > +C++ ObjC++ Var(warn_template_names_tu_local) Warning EnabledBy(Wextra) > > +Warn about templates naming TU-local entities in a module. > > + > > Wterminate > > C++ ObjC++ Warning Var(warn_terminate) Init(1) > > Warn if a throw expression will always result in a call to terminate(). > > diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc > > index cd379514991..b959533bcb1 100644 > > --- a/gcc/cp/cp-objcp-common.cc > > +++ b/gcc/cp/cp-objcp-common.cc > > @@ -233,6 +233,7 @@ cp_tree_size (enum tree_code code) > > case ASSERTION_STMT: return sizeof (tree_exp); > > case PRECONDITION_STMT: return sizeof (tree_exp); > > case POSTCONDITION_STMT: return sizeof (tree_exp); > > + case TU_LOCAL_ENTITY: return sizeof (tree_tu_local_entity); > > default: > > switch (TREE_CODE_CLASS (code)) > > { > > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def > > index 18f75108c7b..7580dc3667d 100644 > > --- a/gcc/cp/cp-tree.def > > +++ b/gcc/cp/cp-tree.def > > @@ -573,6 +573,12 @@ DEFTREECODE (ASSERTION_STMT, "assertion_stmt", > > tcc_statement, 3) > > DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 3) > > DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 4) > > > > +/* A reference to a translation-unit local entity. > > + > > + This is emitted by modules streaming when writing a TU-local entity that > > + wasn't an exposure (e.g. in a non-inline function template). */ > > +DEFTREECODE (TU_LOCAL_ENTITY, "tu_local_entity", tcc_exceptional, 0) > > + > > /* > > Local variables: > > mode:c > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > > index dc153a97dc4..a71d0704f8b 100644 > > --- a/gcc/cp/cp-tree.h > > +++ b/gcc/cp/cp-tree.h > > @@ -1778,6 +1778,22 @@ check_constraint_info (tree t) > > it for unscoped enums. */ > > #define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE) > > > > +/* Represents a streamed-in translation-unit-local entity. Any use of > > + this node when instantiating a template should emit an error. */ > > +struct GTY(()) tree_tu_local_entity { > > + struct tree_base base; > > + tree name; > > + location_t loc; > > +}; > > + > > +/* The name of a translation-unit-local entity. */ > > +#define TU_LOCAL_ENTITY_NAME(NODE) \ > > + (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name) > > + > > +/* The source location of the translation-unit-local entity. */ > > +#define TU_LOCAL_ENTITY_LOCATION(NODE) \ > > + (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->loc) > > + > > > > /* The list of local parameters introduced by this requires-expression, > > in the form of a chain of PARM_DECLs. */ > > @@ -1811,7 +1827,8 @@ enum cp_tree_node_structure_enum { > > TS_CP_LAMBDA_EXPR, > > TS_CP_TEMPLATE_INFO, > > TS_CP_CONSTRAINT_INFO, > > - TS_CP_USERDEF_LITERAL > > + TS_CP_USERDEF_LITERAL, > > + TS_CP_TU_LOCAL_ENTITY > > }; > > > > /* The resulting tree type. */ > > @@ -1842,6 +1859,8 @@ union GTY((desc ("cp_tree_node_structure (&%h)"), > > constraint_info; > > struct tree_userdef_literal GTY ((tag ("TS_CP_USERDEF_LITERAL"))) > > userdef_literal; > > + struct tree_tu_local_entity GTY ((tag ("TS_CP_TU_LOCAL_ENTITY"))) > > + tu_local_entity; > > }; > > > > > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > > index 136ffb1a7ac..4b26dc5d367 100644 > > --- a/gcc/cp/module.cc > > +++ b/gcc/cp/module.cc > > @@ -2330,7 +2330,9 @@ private: > > 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_TU_LOCAL_BIT, /* It is a TU-local entity. */ > > + DB_TU_LOCAL_BIT, /* Is a TU-local entity. */ > > + DB_REFS_TU_LOCAL_BIT, /* Refers to a TU-local entity (but is not > > + necessarily an exposure.) */ > > DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */ > > DB_IMPORTED_BIT, /* An imported entity. */ > > DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ > > @@ -2401,7 +2403,9 @@ public: > > public: > > bool has_defn () const > > { > > - return get_flag_bit<DB_DEFN_BIT> (); > > + /* Never consider TU-local entities as having definitions, since > > + we will never be accessing them from importers anyway. */ > > + return get_flag_bit<DB_DEFN_BIT> () && !is_tu_local (); > > } > > > > public: > > @@ -2416,6 +2420,10 @@ public: > > { > > return get_flag_bit<DB_TU_LOCAL_BIT> (); > > } > > + bool refs_tu_local () const > > + { > > + return get_flag_bit<DB_REFS_TU_LOCAL_BIT> (); > > + } > > bool is_exposure () const > > { > > return get_flag_bit<DB_EXPOSURE_BIT> (); > > @@ -2543,11 +2551,13 @@ public: > > depset *current; /* Current depset being depended. */ > > unsigned section; /* When writing out, the section. */ > > bool reached_unreached; /* We reached an unreached entity. */ > > + bool ignore_tu_local; /* In a context where referencing a TU-local > > + entity is not an exposure. */ > > > > public: > > hash (size_t size, hash *c = NULL) > > : parent (size), chain (c), current (NULL), section (0), > > - reached_unreached (false) > > + reached_unreached (false), ignore_tu_local (false) > > { > > worklist.create (size); > > } > > @@ -2748,6 +2758,7 @@ static GTY((cache)) decl_tree_cache_map > > *imported_temploid_friends; > > /* Tree tags. */ > > enum tree_tag { > > tt_null, /* NULL_TREE. */ > > + tt_tu_local, /* A TU-local entity. */ > > tt_fixed, /* Fixed vector index. */ > > > > tt_node, /* By-value node. */ > > @@ -3032,6 +3043,8 @@ private: > > depset::hash *dep_hash; /* Dependency table. */ > > int ref_num; /* Back reference number. */ > > unsigned section; > > + bool writing_local_entities; /* Whether we might walk into a TU-local > > + entity we need to emit placeholders for. */ > > #if CHECKING_P > > int importedness; /* Checker that imports not occurring > > inappropriately. +ve imports ok, > > @@ -3061,6 +3074,18 @@ public: > > }; > > > > public: > > + /* The walk is used for three similar purposes: > > + > > + 1. The initial scan for dependencies. > > + 2. Once dependencies have been found, ordering them. > > + 3. Writing dependencies to file (streaming_p). > > + > > + For cases where it matters, these accessers can be used to determine > > + which state we're in. */ > > + bool is_initial_scan () const > > + { > > + return !streaming_p () && !is_key_order (); > > + } > > bool is_key_order () const > > { > > return dep_hash->is_key_order (); > > @@ -3101,6 +3126,10 @@ private: > > void tree_pair_vec (vec<tree_pair_s, va_gc> *); > > void tree_list (tree, bool has_purpose); > > > > +private: > > + bool has_tu_local_dep (tree) const; > > + tree find_tu_local_decl (tree); > > + > > public: > > /* Mark a node for by-value walking. */ > > void mark_by_value (tree); > > @@ -3138,7 +3167,7 @@ public: > > > > public: > > /* Serialize various definitions. */ > > - void write_definition (tree decl); > > + void write_definition (tree decl, bool refs_tu_local = false); > > void mark_declaration (tree decl, bool do_defn); > > > > private: > > @@ -3166,6 +3195,7 @@ private: > > static unsigned tree_val_count; > > static unsigned decl_val_count; > > static unsigned back_ref_count; > > + static unsigned tu_local_count; > > static unsigned null_count; > > }; > > } // anon namespace > > @@ -3174,12 +3204,14 @@ private: > > unsigned trees_out::tree_val_count; > > unsigned trees_out::decl_val_count; > > unsigned trees_out::back_ref_count; > > +unsigned trees_out::tu_local_count; > > unsigned trees_out::null_count; > > > > trees_out::trees_out (allocator *mem, module_state *state, depset::hash > > &deps, > > unsigned section) > > :parent (mem), state (state), tree_map (500), > > - dep_hash (&deps), ref_num (0), section (section) > > + dep_hash (&deps), ref_num (0), section (section), > > + writing_local_entities (false) > > { > > #if CHECKING_P > > importedness = 0; > > @@ -4301,6 +4333,9 @@ dumper::impl::nested_name (tree t) > > int origin = -1; > > tree name = NULL_TREE; > > > > + if (t && TREE_CODE (t) == TU_LOCAL_ENTITY) > > + t = TU_LOCAL_ENTITY_NAME (t); > > + > > if (t && TREE_CODE (t) == TREE_BINFO) > > t = BINFO_TYPE (t); > > > > @@ -4853,6 +4888,7 @@ trees_out::instrument () > > dump (" %u decl trees", decl_val_count); > > dump (" %u other trees", tree_val_count); > > dump (" %u back references", back_ref_count); > > + dump (" %u TU-local entities", tu_local_count); > > dump (" %u null trees", null_count); > > } > > } > > @@ -7809,6 +7845,17 @@ trees_out::decl_value (tree decl, depset *dep) > > || DECL_ORIGINAL_TYPE (decl) > > || !TYPE_PTRMEMFUNC_P (TREE_TYPE (decl))); > > > > + /* There's no need to walk any of the contents of a known TU-local > > entity, > > + since importers should never see any of it regardless. But make sure > > we > > + at least note its location so importers can use it for diagnostics. > > */ > > + if (dep && dep->is_tu_local ()) > > + { > > + gcc_checking_assert (is_initial_scan ()); > > + insert (decl, WK_value); > > + state->note_location (DECL_SOURCE_LOCATION (decl)); > > + return; > > + } > > + > > merge_kind mk = get_merge_kind (decl, dep); > > bool is_imported_temploid_friend = imported_temploid_friends->get (decl); > > > > @@ -8390,14 +8437,17 @@ trees_in::decl_value () > > /* Frob it to be ready for cloning. */ > > TREE_TYPE (inner) = DECL_ORIGINAL_TYPE (inner); > > DECL_ORIGINAL_TYPE (inner) = NULL_TREE; > > - set_underlying_type (inner); > > - if (tdef_flags & 2) > > + if (TREE_CODE (TREE_TYPE (inner)) != TU_LOCAL_ENTITY) > > { > > - /* Match instantiate_alias_template's handling. */ > > - tree type = TREE_TYPE (inner); > > - TYPE_DEPENDENT_P (type) = true; > > - TYPE_DEPENDENT_P_VALID (type) = true; > > - SET_TYPE_STRUCTURAL_EQUALITY (type); > > + set_underlying_type (inner); > > + if (tdef_flags & 2) > > + { > > + /* Match instantiate_alias_template's handling. */ > > + tree type = TREE_TYPE (inner); > > + TYPE_DEPENDENT_P (type) = true; > > + TYPE_DEPENDENT_P_VALID (type) = true; > > + SET_TYPE_STRUCTURAL_EQUALITY (type); > > + } > > } > > } > > > > @@ -8902,10 +8952,14 @@ trees_out::decl_node (tree decl, walk_kind ref) > > } > > tree_node (tpl); > > > > - /* Streaming TPL caused us to visit DECL and maybe its type. */ > > - gcc_checking_assert (TREE_VISITED (decl)); > > - if (DECL_IMPLICIT_TYPEDEF_P (decl)) > > - gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl))); > > + /* Streaming TPL caused us to visit DECL and maybe its type, > > + if it wasn't TU-local. */ > > + if (CHECKING_P && !has_tu_local_dep (tpl)) > > + { > > + gcc_checking_assert (TREE_VISITED (decl)); > > + if (DECL_IMPLICIT_TYPEDEF_P (decl)) > > + gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl))); > > + } > > return false; > > } > > > > @@ -8925,10 +8979,10 @@ trees_out::decl_node (tree decl, walk_kind ref) > > dep = dep_hash->add_dependency (decl, kind); > > } > > > > - if (!dep) > > + if (!dep || dep->is_tu_local ()) > > { > > /* Some internal entity of context. Do by value. */ > > - decl_value (decl, NULL); > > + decl_value (decl, dep); > > return false; > > } > > > > @@ -9084,7 +9138,10 @@ trees_out::type_node (tree type) > > if (streaming_p ()) > > dump (dumper::TREE) && dump ("Wrote typedef %C:%N%S", > > TREE_CODE (name), name, name); > > - gcc_checking_assert (TREE_VISITED (type)); > > + > > + /* We'll have either visited this type or have newly discovered > > + that it's TU-local; either way we won't need to visit it again. */ > > + gcc_checking_assert (TREE_VISITED (type) || has_tu_local_dep (name)); > > return; > > } > > > > @@ -9363,6 +9420,64 @@ trees_in::tree_value () > > return existing; > > } > > > > +/* Whether DECL has a TU-local dependency in the hash. */ > > + > > +bool > > +trees_out::has_tu_local_dep (tree decl) const > > +{ > > + /* Only the contexts of fields or enums remember that they're > > + TU-local. */ > > + if (DECL_CONTEXT (decl) > > + && (TREE_CODE (decl) == FIELD_DECL > > + || TREE_CODE (decl) == CONST_DECL)) > > + decl = TYPE_NAME (DECL_CONTEXT (decl)); > > + > > + depset *dep = dep_hash->find_dependency (decl); > > + return dep && dep->is_tu_local (); > > +} > > + > > +/* If T depends on a TU-local entity, return that decl. */ > > + > > +tree > > +trees_out::find_tu_local_decl (tree t) > > +{ > > + /* We need to have walked all deps first before we can check. */ > > + gcc_checking_assert (!is_initial_scan ()); > > + > > + auto walker = [](tree *tp, int *walk_subtrees, void *data) -> tree > > + { > > + auto self = (trees_out *)data; > > + > > + tree decl = NULL_TREE; > > + if (TYPE_P (*tp)) > > + { > > + /* A PMF type is a record type, which we otherwise wouldn't walk; > > + return whether the function type is TU-local. */ > > + if (TYPE_PTRMEMFUNC_P (*tp)) > > + { > > + *walk_subtrees = 0; > > + return self->find_tu_local_decl (TYPE_PTRMEMFUNC_FN_TYPE (*tp)); > > + } > > + else > > + decl = TYPE_MAIN_DECL (*tp); > > + } > > + else if (DECL_P (*tp)) > > + decl = *tp; > > + > > + if (decl) > > + { > > + /* We found a DECL, this will tell us whether we're TU-local. */ > > + *walk_subtrees = 0; > > + return self->has_tu_local_dep (decl) ? decl : NULL_TREE; > > + } > > + return NULL_TREE; > > + }; > > + > > + /* We need to walk without duplicates so that we step into the pointed-to > > + types of array types. */ > > + return cp_walk_tree_without_duplicates (&t, walker, this); > > +} > > + > > /* Stream out tree node T. We automatically create local back > > references, which is essentially a single pass lisp > > self-referential structure pretty-printer. */ > > @@ -9375,6 +9490,46 @@ trees_out::tree_node (tree t) > > if (ref == WK_none) > > goto done; > > > > + /* Find TU-local entities and intercept streaming to instead write a > > + placeholder value; this way we don't need to emit such decls. > > + We only need to do this when writing a definition of an entity > > + that we know names a TU-local entity. */ > > + if (!is_initial_scan () && writing_local_entities) > > + { > > + tree local_decl = NULL_TREE; > > + if (DECL_P (t) && has_tu_local_dep (t)) > > + local_decl = t; > > + /* Consider a type to be TU-local if it refers to any TU-local decl, > > + no matter how deep. > > + > > + This worsens diagnostics slightly, as we often no longer point > > + directly to the at-fault entity when instantiating. However, this > > + reduces the module size slightly and means that much less of pt.cc > > + needs to know about us. */ > > + else if (TYPE_P (t)) > > + local_decl = find_tu_local_decl (t); > > + else if (EXPR_P (t)) > > + local_decl = find_tu_local_decl (TREE_TYPE (t)); > > + > > + if (local_decl) > > + { > > + int tag = insert (t, WK_value); > > + if (streaming_p ()) > > + { > > + tu_local_count++; > > + i (tt_tu_local); > > + dump (dumper::TREE) > > + && 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)); > > + if (state) > > + state->write_location (*this, DECL_SOURCE_LOCATION (local_decl)); > > + goto done; > > + } > > + } > > + > > if (ref != WK_normal) > > goto skip_normal; > > > > @@ -9531,6 +9686,18 @@ trees_in::tree_node (bool is_use) > > /* NULL_TREE. */ > > break; > > > > + case tt_tu_local: > > + { > > + /* A translation-unit-local entity. */ > > + res = make_node (TU_LOCAL_ENTITY); > > + int tag = insert (res); > > + > > + TU_LOCAL_ENTITY_NAME (res) = tree_node (); > > + TU_LOCAL_ENTITY_LOCATION (res) = state->read_location (*this); > > + dump (dumper::TREE) && dump ("Read TU-local entity:%d %N", tag, res); > > + } > > + break; > > + > > case tt_fixed: > > /* A fixed ref, find it in the fixed_ref array. */ > > { > > @@ -10147,7 +10314,8 @@ trees_in::tree_node (bool is_use) > > /* A template. */ > > if (tree tpl = tree_node ()) > > { > > - res = DECL_TEMPLATE_RESULT (tpl); > > + res = (TREE_CODE (tpl) == TU_LOCAL_ENTITY ? > > + tpl : DECL_TEMPLATE_RESULT (tpl)); > > dump (dumper::TREE) > > && dump ("Read template %C:%N", TREE_CODE (res), res); > > } > > @@ -11917,8 +12085,18 @@ void > > trees_out::write_function_def (tree decl) > > { > > tree_node (DECL_RESULT (decl)); > > - tree_node (DECL_INITIAL (decl)); > > - tree_node (DECL_SAVED_TREE (decl)); > > + > > + { > > + /* The function body for a non-inline function or function template > > + is ignored for determining exposures. This should only matter > > + for templates (we don't emit the bodies of non-inline functions > > + to begin with). */ > > + auto ovr = make_temp_override (dep_hash->ignore_tu_local, > > + !DECL_DECLARED_INLINE_P (decl)); > > + tree_node (DECL_INITIAL (decl)); > > + tree_node (DECL_SAVED_TREE (decl)); > > + } > > + > > tree_node (DECL_FRIEND_CONTEXT (decl)); > > > > constexpr_fundef *cexpr = retrieve_constexpr_fundef (decl); > > @@ -12020,6 +12198,10 @@ trees_in::read_function_def (tree decl, tree > > maybe_template) > > void > > trees_out::write_var_def (tree decl) > > { > > + /* The initializer of a variable or variable template is ignored for > > + determining exposures. */ > > + auto ovr = make_temp_override (dep_hash->ignore_tu_local, VAR_P (decl)); > > + > > tree init = DECL_INITIAL (decl); > > tree_node (init); > > if (!init) > > @@ -12207,21 +12389,28 @@ trees_out::write_class_def (tree defn) > > for (; vtables; vtables = TREE_CHAIN (vtables)) > > write_definition (vtables); > > > > - /* Write the friend classes. */ > > - tree_list (CLASSTYPE_FRIEND_CLASSES (type), false); > > + { > > + /* Friend declarations in class definitions are ignored when > > + determining exposures. */ > > + auto ovr = make_temp_override (dep_hash->ignore_tu_local, true); > > > > - /* Write the friend functions. */ > > - for (tree friends = DECL_FRIENDLIST (defn); > > - friends; friends = TREE_CHAIN (friends)) > > - { > > - /* Name of these friends. */ > > - tree_node (TREE_PURPOSE (friends)); > > - tree_list (TREE_VALUE (friends), false); > > - } > > - /* End of friend fns. */ > > - tree_node (NULL_TREE); > > + /* Write the friend classes. */ > > + tree_list (CLASSTYPE_FRIEND_CLASSES (type), false); > > > > - /* Write the decl list. */ > > + /* Write the friend functions. */ > > + for (tree friends = DECL_FRIENDLIST (defn); > > + friends; friends = TREE_CHAIN (friends)) > > + { > > + tree_node (FRIEND_NAME (friends)); > > + tree_list (FRIEND_DECLS (friends), false); > > + } > > + /* End of friend fns. */ > > + tree_node (NULL_TREE); > > + } > > + > > + /* Write the decl list. We don't need to ignore exposures of friend > > + decls here as any such decls should already have been added and > > + ignored above. */ > > tree_list (CLASSTYPE_DECL_LIST (type), true); > > > > if (TYPE_CONTAINS_VPTR_P (type)) > > @@ -12574,6 +12763,8 @@ trees_in::read_class_def (tree defn, tree > > maybe_template) > > friend_decls; friend_decls = TREE_CHAIN (friend_decls)) > > { > > tree f = TREE_VALUE (friend_decls); > > + if (TREE_CODE (f) == TU_LOCAL_ENTITY) > > + continue; > > > > DECL_BEFRIENDING_CLASSES (f) > > = tree_cons (NULL_TREE, type, DECL_BEFRIENDING_CLASSES (f)); > > @@ -12722,8 +12913,11 @@ trees_in::read_enum_def (tree defn, tree > > maybe_template) > > /* Write out the body of DECL. See above circularity note. */ > > > > void > > -trees_out::write_definition (tree decl) > > +trees_out::write_definition (tree decl, bool refs_tu_local) > > { > > + auto ovr = make_temp_override (writing_local_entities, > > + writing_local_entities || refs_tu_local); > > + > > if (streaming_p ()) > > { > > assert_definition (decl); > > @@ -13309,7 +13503,11 @@ depset::hash::add_dependency (depset *dep) > > current->deps.safe_push (dep); > > > > if (dep->is_tu_local ()) > > - current->set_flag_bit<DB_EXPOSURE_BIT> (); > > + { > > + current->set_flag_bit<DB_REFS_TU_LOCAL_BIT> (); > > + if (!ignore_tu_local) > > + current->set_flag_bit<DB_EXPOSURE_BIT> (); > > + } > > > > if (current->get_entity_kind () == EK_USING > > && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) > > @@ -13959,7 +14157,7 @@ depset::hash::find_dependencies (module_state > > *module) > > { > > walker.mark_declaration (decl, current->has_defn ()); > > > > - if (!walker.is_key_order () > > + if (!is_key_order () > > && (item->get_entity_kind () == EK_SPECIALIZATION > > || item->get_entity_kind () == EK_PARTIAL > > || (item->get_entity_kind () == EK_DECL > > @@ -13971,15 +14169,15 @@ depset::hash::find_dependencies (module_state > > *module) > > > > walker.decl_value (decl, current); > > if (current->has_defn ()) > > - walker.write_definition (decl); > > + walker.write_definition (decl, current->refs_tu_local ()); > > } > > walker.end (); > > > > - if (!walker.is_key_order () > > + if (!is_key_order () > > && DECL_CLASS_TEMPLATE_P (decl)) > > add_deduction_guides (decl); > > > > - if (!walker.is_key_order () > > + if (!is_key_order () > > && TREE_CODE (decl) == TEMPLATE_DECL > > && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) > > { > > @@ -14168,6 +14366,37 @@ depset::hash::finalize_dependencies () > > /* We should have emitted an error above. */ > > gcc_checking_assert (explained); > > } > > + else if (warn_template_names_tu_local > > + && dep->refs_tu_local () && !dep->is_tu_local ()) > > + { > > + tree decl = dep->get_entity (); > > + > > + /* Friend decls in a class body are ignored, but this is harmless: > > + it should not impact any consumers. */ > > + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) > > + continue; > > + > > + /* We should now only be warning about templates. */ > > + gcc_checking_assert > > + (TREE_CODE (decl) == TEMPLATE_DECL > > + && VAR_OR_FUNCTION_DECL_P (DECL_TEMPLATE_RESULT (decl))); > > + > > + /* Ideally we would only warn in cases where there are no explicit > > + instantiations of the template, but we don't currently track this > > + in an easy-to-find way. */ > > + for (depset *rdep : dep->deps) > > + if (!rdep->is_binding () && rdep->is_tu_local ()) > > + { > > + tree ref = rdep->get_entity (); > > + auto_diagnostic_group d; > > + if (warning_at (DECL_SOURCE_LOCATION (decl), > > + OPT_Wtemplate_names_tu_local, > > + "%qD refers to TU-local entity %qD and cannot " > > + "be instantiated in other TUs", decl, ref)) > > + is_tu_local_entity (ref, /*explain=*/true); > > + break; > > + } > > + } > > } > > > > return ok; > > @@ -15331,8 +15560,9 @@ enum ct_bind_flags > > void > > module_state::intercluster_seed (trees_out &sec, unsigned index_hwm, > > depset *dep) > > { > > - if (dep->is_import () > > - || dep->cluster < index_hwm) > > + if (dep->is_tu_local ()) > > + /* We only stream placeholders for TU-local entities anyway. */; > > + else if (dep->is_import () || dep->cluster < index_hwm) > > { > > tree ent = dep->get_entity (); > > if (!TREE_VISITED (ent)) > > @@ -15553,7 +15783,7 @@ module_state::write_cluster (elf_out *to, depset > > *scc[], unsigned size, > > sec.u (ct_defn); > > sec.tree_node (decl); > > dump () && dump ("Writing definition %N", decl); > > - sec.write_definition (decl); > > + sec.write_definition (decl, b->refs_tu_local ()); > > > > if (!namer->has_defn ()) > > namer = b; > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 03a1144765b..c7c6da0969e 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -9881,6 +9881,71 @@ add_pending_template (tree d) > > pop_tinst_level (); > > } > > > > +/* Emit a diagnostic about instantiating a reference to TU-local entity E. > > */ > > + > > +static void > > +complain_about_tu_local_entity (tree e) > > +{ > > + auto_diagnostic_group d; > > + error ("instantiation exposes TU-local entity %qD", > > + TU_LOCAL_ENTITY_NAME (e)); > > + inform (TU_LOCAL_ENTITY_LOCATION (e), "declared here"); > > +} > > + > > +/* Checks if T contains a TU-local entity. */ > > + > > +static bool > > +expr_contains_tu_local_entity (tree t) > > +{ > > + if (!modules_p ()) > > + return false; > > + > > + auto walker = [](tree *tp, int *walk_subtrees, void *) -> tree > > + { > > + if (TREE_CODE (*tp) == TU_LOCAL_ENTITY) > > + return *tp; > > + if (!EXPR_P (*tp)) > > + *walk_subtrees = false; > > + return NULL_TREE; > > + }; > > + return cp_walk_tree (&t, walker, nullptr, nullptr); > > +} > > + > > +/* Errors and returns TRUE if X is a function that contains a TU-local > > + entity in its overload set. */ > > + > > +static bool > > +function_contains_tu_local_entity (tree x) > > +{ > > + if (!modules_p ()) > > + return false; > > + > > + if (!x || x == error_mark_node) > > + return false; > > + > > + if (TREE_CODE (x) == OFFSET_REF > > + || TREE_CODE (x) == COMPONENT_REF) > > + x = TREE_OPERAND (x, 1); > > + x = MAYBE_BASELINK_FUNCTIONS (x); > > + if (TREE_CODE (x) == TEMPLATE_ID_EXPR) > > + x = TREE_OPERAND (x, 0); > > + > > + if (OVL_P (x)) > > + for (tree ovl : lkp_range (x)) > > + if (TREE_CODE (ovl) == TU_LOCAL_ENTITY) > > + { > > + x = ovl; > > + break; > > + } > > + > > + if (TREE_CODE (x) == TU_LOCAL_ENTITY) > > + { > > + complain_about_tu_local_entity (x); > > + return true; > > + } > > + > > + return false; > > +} > > > > /* Return a TEMPLATE_ID_EXPR corresponding to the indicated FNS and > > ARGLIST. Valid choices for FNS are given in the cp-tree.def > > @@ -12743,8 +12808,10 @@ instantiate_class_template (tree type) > > } > > else > > { > > - if (TYPE_P (t) || DECL_CLASS_TEMPLATE_P (t) > > - || DECL_TEMPLATE_TEMPLATE_PARM_P (t)) > > + if (TREE_CODE (t) == TU_LOCAL_ENTITY) > > + /* Ignore. */; > > + else if (TYPE_P (t) || DECL_CLASS_TEMPLATE_P (t) > > + || DECL_TEMPLATE_TEMPLATE_PARM_P (t)) > > { > > /* Build new CLASSTYPE_FRIEND_CLASSES. */ > > > > @@ -15522,7 +15589,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t > > complain, > > RETURN (error_mark_node); > > > > if (TREE_CODE (t) == TYPE_DECL > > - && t == TYPE_MAIN_DECL (TREE_TYPE (t))) > > + && (TREE_CODE (TREE_TYPE (t)) == TU_LOCAL_ENTITY > > + || t == TYPE_MAIN_DECL (TREE_TYPE (t)))) > > { > > /* If this is the canonical decl, we don't have to > > mess with instantiations, and often we can't (for > > @@ -16250,6 +16318,14 @@ tsubst (tree t, tree args, tsubst_flags_t > > complain, tree in_decl) > > || TREE_CODE (t) == TRANSLATION_UNIT_DECL) > > return t; > > > > + /* Any instantiation of a template containing a TU-local entity is an > > + exposure, so always issue a hard error irrespective of complain. */ > > + if (TREE_CODE (t) == TU_LOCAL_ENTITY) > > + { > > + complain_about_tu_local_entity (t); > > + return error_mark_node; > > + } > > + > > tsubst_flags_t tst_ok_flag = (complain & tf_tst_ok); > > complain &= ~tf_tst_ok; > > > > @@ -18486,6 +18562,12 @@ dependent_operand_p (tree t) > > { > > while (TREE_CODE (t) == IMPLICIT_CONV_EXPR) > > t = TREE_OPERAND (t, 0); > > + > > + /* If we contain a TU_LOCAL_ENTITY assume we're non-dependent; we'll > > error > > + later when instantiating. */ > > + if (expr_contains_tu_local_entity (t)) > > + return false; > > I think it'd be more robust and cheaper (avoiding a separate tree walk) > to teach the general constexpr/dependence predicates about > TU_LOCAL_ENTITY instead of handling it only here. >
This was what I'd initially tried, but had ran into some issues with handling call expressions I believe, and felt that this was a simpler way to avoid needing too many special cases and touching more things. That said I agree that this would probably be nicer! I'll try to take another look at this if I get a chance. > > + > > ++processing_template_decl; > > bool r = (potential_constant_expression (t) > > ? value_dependent_expression_p (t) > > @@ -20255,6 +20337,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t > > complain, tree in_decl) > > else > > object = NULL_TREE; > > > > + if (function_contains_tu_local_entity (templ)) > > + RETURN (error_mark_node); > > + > > tree tid = lookup_template_function (templ, targs); > > protected_set_expr_location (tid, EXPR_LOCATION (t)); > > > > @@ -20947,6 +21032,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t > > complain, tree in_decl) > > qualified_p = true; > > } > > > > + if (function_contains_tu_local_entity (function)) > > + RETURN (error_mark_node); > > Similarly, maybe it'd suffice to check this more generally in the > OVERLOAD case of tsubst_expr? > Good thought, I'll give this a shot at some point. > > + > > nargs = call_expr_nargs (t); > > releasing_vec call_args; > > tsubst_call_args (t, args, complain, in_decl, call_args); > > @@ -21968,6 +22056,10 @@ tsubst_expr (tree t, tree args, tsubst_flags_t > > complain, tree in_decl) > > RETURN (op); > > } > > > > + case TU_LOCAL_ENTITY: > > + complain_about_tu_local_entity (t); > > + RETURN (error_mark_node); > > + > > default: > > /* Handle Objective-C++ constructs, if appropriate. */ > > if (tree subst = objcp_tsubst_expr (t, args, complain, in_decl)) > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > > index d38c1feb86f..b5f767f6f12 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 > > +-Wno-template-id-cdtor -Wtemplate-names-tu-local > > -Wno-terminate -Wno-vexing-parse -Wvirtual-inheritance > > -Wno-virtual-move-assign -Wvolatile -Wzero-as-null-pointer-constant} > > > > @@ -4686,6 +4686,23 @@ template<typename T> struct S @{ > > @option{-Wtemplate-id-cdtor} is enabled by default with > > @option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}. > > > > +@opindex Wtemplate-names-tu-local > > +@opindex Wno-template-names-tu-local > > +@item -Wtemplate-names-tu-local > > +Warn when a template body hides an exposure of a translation-unit-local > > +entity. In most cases, referring to a translation-unit-local entity > > +(such as an internal linkage declaration) within an entity that is > > +emitted into a module's CMI is an error. However, within the > > +initializer of a variable, or in the body of a non-inline function, > > +this is not an exposure and no error is emitted. > > + > > +This can cause variable or function templates to accidentally become > > +unusable if they reference such an entity, because other translation > > +units that import the template will never be able to instantiate it. > > +This warning attempts to detect cases where this might occur. > > + > > +This flag is enabled by @option{-Wextra}. > > + > > @opindex Wterminate > > @opindex Wno-terminate > > @item -Wno-terminate @r{(C++ and Objective-C++ only)} > > diff --git a/gcc/testsuite/g++.dg/modules/internal-5_a.C > > b/gcc/testsuite/g++.dg/modules/internal-5_a.C > > new file mode 100644 > > index 00000000000..c5ef3752f5a > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-5_a.C > > @@ -0,0 +1,110 @@ > > +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } > > +// { dg-module-cmi M } > > +// Ignore exposures in these cases > > + > > +export module M; > > + > > +namespace { > > + inline namespace ns { > > + struct internal_t {}; > > + template <typename T> struct internal_tmpl_t {}; > > + > > + int internal_x; > > + void internal_ovl(int&) {} > > + void internal_ovl(internal_t) {} > > + > > + template <typename T> void internal_tmpl() {} > > + } > > +} > > +export struct ok_inst_tag {}; > > + > > + > > +// The function body for a non-inline function or function template > > +export void function() { > > + internal_t i {}; > > + internal_tmpl_t<int> ii {}; > > + internal_ovl(internal_x); > > + internal_tmpl<int>(); > > +} > > + > > +export template <typename T> void function_tmpl() { // { dg-warning > > "refers to TU-local entity" } > > + internal_t i {}; > > + internal_tmpl_t<T> ii {}; > > + internal_ovl(internal_x); > > + internal_tmpl<T>(); > > +} > > +template void function_tmpl<ok_inst_tag>(); > > +template <> void function_tmpl<ok_inst_tag*>() {} > > + > > + > > +// The initializer for a variable or variable template > > +export int var > > + = (internal_t{}, internal_tmpl_t<int>{}, > > + internal_ovl(internal_x), internal_tmpl<int>(), 0); > > + > > +export template <typename T> int var_tmpl // { dg-warning "refers to > > TU-local entity" } > > + = (internal_t{}, internal_tmpl_t<T>{}, > > + internal_ovl(internal_x), internal_tmpl<T>(), 0); > > + > > +template <typename T> int var_tmpl<T*> // { dg-warning "refers to > > TU-local entity" } > > + = (internal_t{}, internal_tmpl_t<T*>{}, > > + internal_ovl(internal_x), internal_tmpl<T*>(), 0); > > + > > +template int var_tmpl<ok_inst_tag>; > > +template <> int var_tmpl<ok_inst_tag*> = 0; > > + > > +export int& constant_ref = internal_x; > > +static_assert (&constant_ref == &internal_x); > > + > > + > > +// Friend declarations in a class definition > > +export struct klass { // { dg-bogus "TU-local" } > > + friend ns::internal_t; > > + friend ns::internal_tmpl_t<int>; > > + friend void ns::internal_ovl(int&); > > + friend void ns::internal_ovl(internal_t); > > + friend void ns::internal_tmpl<int>(); > > + > > + template <typename> friend struct ns::internal_tmpl_t; > > + template <typename> friend void ns::internal_tmpl(); > > +}; > > + > > +export template <typename T> > > +class klass_tmpl { // { dg-bogus "TU-local" } > > + friend ns::internal_t; > > + friend ns::internal_tmpl_t<int>; > > + friend void ns::internal_ovl(int&); > > + friend void ns::internal_ovl(internal_t); > > + friend void ns::internal_tmpl<int>(); > > + > > + template <typename> friend struct ns::internal_tmpl_t; > > + template <typename> friend void ns::internal_tmpl(); > > +}; > > + > > +template <typename T> class klass_tmpl<T*> { // { dg-bogus "TU-local" } > > + friend ns::internal_t; > > + friend ns::internal_tmpl_t<int>; > > + friend void ns::internal_ovl(int&); > > + friend void ns::internal_ovl(internal_t); > > + friend void ns::internal_tmpl<int>(); > > + > > + template <typename> friend struct ns::internal_tmpl_t; > > + template <typename> friend void ns::internal_tmpl(); > > +}; > > + > > + > > +// Any reference to a non-volatile const object or reference with internal > > or > > +// no linkage initialized with a constant expression that is not an ODR-use > > +static const int value = 123; > > +static const int& ref = 456; > > +static const internal_t internal {}; > > +void f(int) {} > > +export inline void no_odr_use() { > > + int x = value; > > + int y = ref; > > + int z = (internal, 0); > > + > > + value; > > + bool b = value < value; > > + f(value); > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/internal-5_b.C > > b/gcc/testsuite/g++.dg/modules/internal-5_b.C > > new file mode 100644 > > index 00000000000..baf60fdafa2 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-5_b.C > > @@ -0,0 +1,30 @@ > > +// { dg-additional-options "-fmodules-ts" } > > + > > +import M; > > + > > +int main() { > > + // These are all OK > > + function(); > > + int a = var; > > + klass k; > > + klass_tmpl<int> kt; > > + klass_tmpl<int*> ktp; > > + no_odr_use(); > > + > > + function_tmpl<ok_inst_tag>(); > > + function_tmpl<ok_inst_tag*>(); > > + int b = var_tmpl<ok_inst_tag>; > > + int c = var_tmpl<ok_inst_tag*>; > > + > > + // But don't ignore exposures in these cases > > + function_tmpl<int>(); // { dg-message "required from here" } > > + int x = var_tmpl<int>; // { dg-message "required from here" } > > + int y = var_tmpl<int*>; // { dg-message "required from here" } > > + > > + // And decls initialized to a TU-local value are not constant here > > + // Unfortunately the error does not currently point to this decl > > + constexpr int& r = constant_ref; > > + // { dg-error "is not a constant expression" "" { target *-*-* } 0 } > > +} > > + > > +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } > > 0 } > > diff --git a/gcc/testsuite/g++.dg/modules/internal-6.C > > b/gcc/testsuite/g++.dg/modules/internal-6.C > > new file mode 100644 > > index 00000000000..0f138781ad5 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-6.C > > @@ -0,0 +1,24 @@ > > +// { dg-additional-options "-fmodules-ts" } > > +// { dg-module-cmi !M } > > +// Exposures (or not) of TU-local values > > + > > +export module M; > > + > > +static void f() {} > > +auto& fr = f; // OK > > +constexpr auto& fr2 = fr; // { dg-error "initialized to a TU-local value" > > } > > +static constexpr auto fp2 = fr; // OK > > + > > +struct S { void (&ref)(); } s{ f }; // OK, value is TU-local > > +constexpr extern struct W { S& s; } wrap{ s }; // OK, value is not > > TU-local > > +constexpr S s2{ f }; // { dg-error "initialized to a TU-local value" } > > + > > +constexpr int a = 123; > > +static constexpr int b = 456; > > +struct X { > > + union { > > + const int* p[2]; > > + }; > > +}; > > +constexpr X x { &a }; // OK > > +constexpr X y { &a, &b }; // { dg-error "initialized to a TU-local value" > > } > > diff --git a/gcc/testsuite/g++.dg/modules/internal-7_a.C > > b/gcc/testsuite/g++.dg/modules/internal-7_a.C > > new file mode 100644 > > index 00000000000..39f53ea382e > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-7_a.C > > @@ -0,0 +1,75 @@ > > +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } > > +// Test streaming and instantiations of various kinds of exposures > > + > > +export module M; > > + > > +namespace { > > + int x; > > + constexpr int y = 1; > > + > > + struct S { int m; void d(); }; > > + enum class E { e }; > > + > > + template <typename T> int f(T t) { return (int)t; } > > +} > > + > > +template <auto N> void g() {} > > + > > +template <typename T> > > +int expose_1() { // { dg-warning "TU-local" } > > + return x; > > +} > > + > > +template <typename T> > > +void expose_2() { // { dg-warning "TU-local" } > > + T t = &y; > > +} > > + > > +template <typename T> > > +bool expose_3() { // { dg-warning "TU-local" } > > + return !(T{} * (x + 5) > 123); > > +} > > + > > +template <typename T> > > +bool expose_4() { // { dg-warning "TU-local" } > > + return __is_same(S, T); > > +} > > + > > +template <typename T> > > +void expose_5() { // { dg-warning "TU-local" } > > + static_assert(T{} == (int)E::e); > > +} > > + > > +template <typename T> > > +void expose_6() { // { dg-warning "TU-local" } > > + f(T{}); > > +} > > + > > +template <typename T> > > +void expose_7() { // { dg-warning "TU-local" } > > + g<&y>(); > > +} > > + > > +template <typename T> > > +void expose_8() { // { dg-warning "TU-local" } > > + decltype(T{} .* &S::m)* (*x)[5][10]; > > +}; > > + > > +template <typename T> > > +bool expose_9() { // { dg-warning "TU-local" } > > + return noexcept((T{} .* &S::d)()); > > +} > > + > > +template <typename T> > > +void expose_10() { // { dg-warning "TU-local" } > > + using U = decltype(f<T>()); > > +} > > + > > +template <typename T> > > +void expose_11() { // { dg-warning "TU-local" } > > + static thread_local E r; > > +} > > + > > +template <typename T> > > +int expose_var // { dg-warning "TU-local" } > > + = f(sizeof(T)); > > diff --git a/gcc/testsuite/g++.dg/modules/internal-7_b.C > > b/gcc/testsuite/g++.dg/modules/internal-7_b.C > > new file mode 100644 > > index 00000000000..2a11e449d6e > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-7_b.C > > @@ -0,0 +1,21 @@ > > +// { dg-additional-options "-fmodules-ts" } > > + > > +module M; > > + > > +void inst() { > > + expose_1<int>(); // { dg-message "required from here" } > > + expose_2<int>(); // { dg-message "required from here" } > > + expose_3<int>(); // { dg-message "required from here" } > > + expose_4<int>(); // { dg-message "required from here" } > > + expose_5<int>(); // { dg-message "required from here" } > > + expose_6<int>(); // { dg-message "required from here" } > > + expose_7<int>(); // { dg-message "required from here" } > > + expose_8<int>(); // { dg-message "required from here" } > > + expose_9<int>(); // { dg-message "required from here" } > > + expose_10<int>(); // { dg-message "required from here" } > > + expose_11<int>(); // { dg-message "required from here" } > > + > > + expose_var<int>; // { dg-message "required from here" } > > +} > > + > > +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } > > 0 } > > diff --git a/gcc/testsuite/g++.dg/modules/internal-8_a.C > > b/gcc/testsuite/g++.dg/modules/internal-8_a.C > > new file mode 100644 > > index 00000000000..46e07a23cf0 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/internal-8_a.C > > @@ -0,0 +1,35 @@ > > +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } > > +// Non-ODR usages of const variables currently are erroneously > > +// reported in templates and constexpr functions; this test > > +// XFAILS them until we can implement a fix. > > + > > +export module M; > > + > > +namespace { struct internal_t {}; }; > > +static const int value = 123; > > +static const int& ref = 456; > > +static const internal_t internal {}; > > + > > +constexpr void f(int) {} > > + > > +export constexpr > > +void no_odr_use_cexpr() { // { dg-bogus "TU-local" "" { xfail *-*-* } } > > + int x = value; > > + int y = ref; > > + int z = (internal, 0); > > + > > + value; > > + bool b = value < value; > > + f(value); > > +} > > + > > +export template <typename T> > > +void no_odr_use_templ() { // { dg-bogus "TU-local" "" { xfail *-*-* } } > > + int x = value; > > + int y = ref; > > + int z = (internal, 0); > > + > > + value; > > + bool b = value < value; > > + f(value); > > +} > > diff --git a/gcc/testsuite/g++.dg/modules/xtreme-header-8.C > > b/gcc/testsuite/g++.dg/modules/xtreme-header-8.C > > new file mode 100644 > > index 00000000000..82c0b59fefe > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/modules/xtreme-header-8.C > > @@ -0,0 +1,8 @@ > > +// PR c++/115126 > > +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } > > +// { dg-module-cmi xstd } > > + > > +export module xstd; > > +extern "C++" { > > + #include "xtreme-header.h" > > +} > > -- > > 2.46.0 > > > > >