On Tue, Sep 24, 2024 at 09:45:37AM +1000, Nathaniel Shead wrote: > I feel like there should be a way to make use of LAMBDA_TYPE_EXTRA_SCOPE to > avoid the need for the new TYPE_DEFINED_IN_INITIALIZER_P flag, perhaps once > something like my patch here[1] is accepted (but with further embellishments > for concepts, probably), but I wasn't able to work it out. Since currently as > far as I'm aware only lambdas can satisfy being a type with no name defined in > an 'initializer' this does seem a little overkill but I've applied it to all > class types just in case. > > [1]: https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662393.html > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > -- >8 -- > > Currently, the modules streaming code implements some checks for > declarations in the CMI that reference (some kinds of) internal-linkage > entities, and errors if so. This patch expands on that support to > implement the logic for exposures of TU-local entities as defined in > [basic.link] since P1815. > > This will cause some code that previously errored in modules to start > compiling; for instance, template specialisations of internal linkage > functions. > > However, some code that previously appeared valid will with this patch > no longer compile, notably some kinds of usages of internal linkage > functions included from the GMF. This appears to be related to P2808 > and FR-025, however as yet there doesn't appear to be consensus for > changing these rules so I've implemented them as-is. > > This patch leaves a couple of things out. In particular, a couple of > the rules for what is a TU-local entity currently seem to me to be > redundant; I've left them as FIXMEs to be handled once I can find > testcases that aren't adequately supported by the other logic here. > > Additionally, there are some exceptions for when naming a TU-local > entity is not always an exposure; I've left support for this to a > follow-up patch for easier review, as it has broader implications for > streaming. > > Finally, this patch makes a couple of small adjustments to the modules > streaming logic to prune any leftover TU-local deps (that aren't > erroneous exposures). This is required for this patch to ensure that > later stages don't get confused by any leftover TU-local entities > floating around. > > gcc/cp/ChangeLog: > > * cp-tree.h (TYPE_DEPENDENT_P_VALID): Fix whitespace. > (TYPE_DEFINED_IN_INITIALIZER_P): New accessor. > * module.cc (DB_IS_INTERNAL_BIT): Rename to... > (DB_TU_LOCAL_BIT): ...this. > (DB_REFS_INTERNAL_BIT): Rename to... > (DB_EXPOSURE_BIT): ...this. > (depset::hash::is_internal): Rename to... > (depset::hash::is_tu_local): ...this. > (depset::hash::refs_internal): Rename to... > (depset::hash::is_exposure): ...this. > (depset::hash::is_tu_local_entity): New function. > (depset::hash::has_tu_local_tmpl_arg): New function. > (depset::hash::is_tu_local_value): New function. > (depset::hash::make_dependency): Check for TU-local entities. > (depset::hash::add_dependency): Make current an exposure > whenever it references a TU-local entity. > (depset::hash::add_binding_entity): Don't create bindings for > any TU-local entity. > (depset::hash::finalize_dependencies): Rename flags and adjust > diagnostic messages to report exposures of TU-local entities. > (depset::tarjan::connect): Don't include any TU-local depsets. > (depset::hash::connect): Likewise. > * parser.h (struct cp_parser::in_initializer_p): New flag. > * parser.cc (cp_debug_parser): Print the new flag. > (cp_parser_new): Set the new flag to false. > (cp_parser_lambda_expression): Mark whether the lambda was > defined in an initializer. > (cp_parser_initializer): Set the new flag to true while parsing. > (cp_parser_class_head): Mark whether the class was defined in an > initializer. > (cp_parser_concept_definition): Set the new flag to true while > parsing. > > gcc/testsuite/ChangeLog: > > * g++.dg/modules/block-decl-2.C: Adjust messages. > * g++.dg/modules/internal-1.C: Adjust messages, remove XFAILs. > * g++.dg/modules/linkage-2.C: Adjust messages, remove XFAILS. > * g++.dg/modules/internal-3.C: New test. > * g++.dg/modules/internal-4.C: New test. > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> > --- > gcc/cp/cp-tree.h | 7 +- > gcc/cp/module.cc | 388 +++++++++++++++++--- > gcc/cp/parser.cc | 16 + > gcc/cp/parser.h | 3 + > gcc/testsuite/g++.dg/modules/block-decl-2.C | 2 +- > gcc/testsuite/g++.dg/modules/internal-1.C | 15 +- > gcc/testsuite/g++.dg/modules/internal-3.C | 18 + > gcc/testsuite/g++.dg/modules/internal-4.C | 112 ++++++ > gcc/testsuite/g++.dg/modules/linkage-2.C | 5 +- > 9 files changed, 493 insertions(+), 73 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/modules/internal-3.C > create mode 100644 gcc/testsuite/g++.dg/modules/internal-4.C > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 7baa2ccbe1e..99fbd905896 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -534,6 +534,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; > AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM) > TEMPLATE_TEMPLATE_PARM_SIMPLE_P (in TEMPLATE_TEMPLATE_PARM) > 6: TYPE_DEPENDENT_P_VALID > + 7: TYPE_DEFINED_IN_INITIALIZER_P > > Usage of DECL_LANG_FLAG_?: > 0: DECL_TEMPLATE_PARM_P (in PARM_DECL, CONST_DECL, TYPE_DECL, or > TEMPLATE_DECL) > @@ -2292,7 +2293,11 @@ enum languages { lang_c, lang_cplusplus }; > > /* True if dependent_type_p has been called for this type, with the > result that TYPE_DEPENDENT_P is valid. */ > -#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6(NODE) > +#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6 (NODE) > + > +/* True if this type was defined in an initializer. Used for determining > + whether an entity is TU-local. */ > +#define TYPE_DEFINED_IN_INITIALIZER_P(NODE) TYPE_LANG_FLAG_7 (NODE) > > /* Nonzero if this type is const-qualified. */ > #define CP_TYPE_CONST_P(NODE) \ > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index 7589de2348d..ff6891ff8d6 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -2330,10 +2330,8 @@ 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_IS_INTERNAL_BIT, /* It is an (erroneous) > - internal-linkage entity. */ > - DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage > - entity. */ > + DB_TU_LOCAL_BIT, /* It is a TU-local entity. */ > + DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */ > DB_IMPORTED_BIT, /* An imported entity. */ > DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ > DB_HIDDEN_BIT, /* A hidden binding. */ > @@ -2414,13 +2412,13 @@ public: > return get_flag_bit<DB_IS_MEMBER_BIT> (); > } > public: > - bool is_internal () const > + bool is_tu_local () const > { > - return get_flag_bit<DB_IS_INTERNAL_BIT> (); > + return get_flag_bit<DB_TU_LOCAL_BIT> (); > } > - bool refs_internal () const > + bool is_exposure () const > { > - return get_flag_bit<DB_REFS_INTERNAL_BIT> (); > + return get_flag_bit<DB_EXPOSURE_BIT> (); > } > bool is_import () const > { > @@ -2580,6 +2578,11 @@ public: > depset *add_dependency (tree decl, entity_kind); > void add_namespace_context (depset *, tree ns); > > + private: > + bool has_tu_local_tmpl_arg (tree decl, tree args, bool explain); > + bool is_tu_local_entity (tree decl, bool explain = false); > + bool is_tu_local_value (tree decl, tree expr, bool explain = false); > + > private: > static bool add_binding_entity (tree, WMB_Flags, void *); > > @@ -9528,6 +9531,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; > +
I just noticed I messed up rebasing somehow, this hunk should be part of the following patch. > case tt_fixed: > /* A fixed ref, find it in the fixed_ref array. */ > { > @@ -12890,6 +12905,243 @@ depset::hash::find_binding (tree ctx, tree name) > return slot ? *slot : NULL; > } > > +/* Returns true if DECL is a TU-local entity, as defined by [basic.link]. > + If EXPLAIN is true, emit an informative note about why DECL is TU-local. > */ > + > +bool > +depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/) > +{ > + gcc_checking_assert (DECL_P (decl)); > + > + /* An explicit type alias is not an entity, and so is never TU-local. */ > + if (TREE_CODE (decl) == TYPE_DECL > + && !DECL_IMPLICIT_TYPEDEF_P (decl) > + && !DECL_SELF_REFERENCE_P (decl)) > + return false; > + > + location_t loc = DECL_SOURCE_LOCATION (decl); > + tree type = TREE_TYPE (decl); > + > + /* Check specializations first for slightly better explanations. */ > + int use_tpl = -1; > + tree ti = node_template_info (decl, use_tpl); > + if (use_tpl > 0 && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL) > + { > + /* A specialization of a TU-local template. */ > + tree tmpl = TI_TEMPLATE (ti); > + if (is_tu_local_entity (tmpl)) > + { > + if (explain) > + { > + inform (loc, "%qD is a specialization of TU-local template %qD", > + decl, tmpl); > + is_tu_local_entity (tmpl, /*explain=*/true); > + } > + return true; > + } > + > + /* A specialization of a template with any TU-local template argument. > */ > + if (has_tu_local_tmpl_arg (decl, TI_ARGS (ti), explain)) > + return true; > + > + /* FIXME A specialization of a template whose (possibly instantiated) > + declaration is an exposure. This should always be covered by the > + above cases?? */ > + } > + > + /* A type, function, variable, or template with internal linkage. */ > + linkage_kind kind = decl_linkage (decl); > + if (kind == lk_internal) > + { > + if (explain) > + inform (loc, "%qD declared with internal linkage", decl); > + return true; > + } > + > + /* Does not have a name with linkage and is declared, or introduced by a > + lambda-expression, within the definition of a TU-local entity. */ > + if (kind == lk_none) > + { > + tree ctx = CP_DECL_CONTEXT (decl); > + if (LAMBDA_TYPE_P (type)) > + if (tree extra = LAMBDA_TYPE_EXTRA_SCOPE (type)) > + ctx = extra; > + > + if (TREE_CODE (ctx) == NAMESPACE_DECL) > + { > + if (!TREE_PUBLIC (ctx)) > + { > + if (explain) > + inform (loc, "%qD has no linkage and is declared in an " > + "anonymous namespace", decl); > + return true; > + } > + } > + else if (TYPE_P (ctx)) > + { > + tree ctx_decl = TYPE_MAIN_DECL (ctx); > + if (is_tu_local_entity (ctx_decl)) > + { > + if (explain) > + { > + inform (loc, "%qD has no linkage and is declared within " > + "TU-local entity %qT", decl, ctx); > + is_tu_local_entity (ctx_decl, /*explain=*/true); > + } > + return true; > + } > + } > + else if (is_tu_local_entity (ctx)) > + { > + if (explain) > + { > + inform (loc, "%qD has no linkage and is declared within " > + "TU-local entity %qD", decl, ctx); > + is_tu_local_entity (ctx, /*explain=*/true); > + } > + return true; > + } > + } > + > + /* A type with no name that is defined outside a class-specifier, function > + body, or initializer; or is introduced by a defining-type-specifier that > + is used to declare only TU-local entities. > + > + We consider types with names for linkage purposes as having names, since > + these aren't really TU-local, and also consider constraint-expressions > + as initializers. */ > + if (TREE_CODE (decl) == TYPE_DECL > + && TYPE_ANON_P (type) > + && !DECL_SELF_REFERENCE_P (decl) > + /* An enum with an enumerator name for linkage. */ > + && !(UNSCOPED_ENUM_P (type) && TYPE_VALUES (type))) > + { > + tree main_decl = TYPE_MAIN_DECL (type); > + if (!TYPE_DEFINED_IN_INITIALIZER_P (type) > + && !DECL_CLASS_SCOPE_P (main_decl) > + && !decl_function_context (main_decl)) > + { > + if (explain) > + inform (loc, "%qT has no name and is not defined within a class, " > + "function, or initializer", type); > + return true; > + } > + > + // FIXME introduced by a defining-type-specifier only declaring > TU-local > + // entities; does this refer to e.g. 'static struct {} a;"? I can't > + // think of any cases where this isn't covered by earlier cases. */ > + } > + > + return false; > +} > + > +/* Helper for is_tu_local_entity. Returns true if one of the ARGS of > + DECL is TU-local. Emits an explanation if EXPLAIN is true. */ > + > +bool > +depset::hash::has_tu_local_tmpl_arg (tree decl, tree args, bool explain) > +{ > + if (!args || TREE_CODE (args) != TREE_VEC) > + return false; > + > + for (tree a : tree_vec_range (args)) > + { > + if (TREE_CODE (a) == TREE_VEC) > + { > + if (has_tu_local_tmpl_arg (decl, a, explain)) > + return true; > + } > + else if (!WILDCARD_TYPE_P (a)) > + { > + if (DECL_P (a) && is_tu_local_entity (a)) > + { > + if (explain) > + { > + inform (DECL_SOURCE_LOCATION (decl), > + "%qD has TU-local template argument %qD", > + decl, a); > + is_tu_local_entity (a, /*explain=*/true); > + } > + return true; > + } > + > + if (TYPE_P (a) && TYPE_NAME (a) && is_tu_local_entity (TYPE_NAME (a))) > + { > + if (explain) > + { > + inform (DECL_SOURCE_LOCATION (decl), > + "%qD has TU-local template argument %qT", > + decl, a); > + is_tu_local_entity (TYPE_NAME (a), /*explain=*/true); > + } > + return true; > + } > + > + if (EXPR_P (a) && is_tu_local_value (decl, a, explain)) > + return true; > + } > + } > + > + return false; > +} > + > +/* Returns true if EXPR (part of the initializer for DECL) is a TU-local > value > + or object. Emits an explanation if EXPLAIN is true. */ > + > +bool > +depset::hash::is_tu_local_value (tree decl, tree expr, bool explain) > +{ > + if (!expr) > + return false; > + > + tree e = expr; > + STRIP_ANY_LOCATION_WRAPPER (e); > + STRIP_NOPS (e); > + if (TREE_CODE (e) == TARGET_EXPR) > + e = TARGET_EXPR_INITIAL (e); > + if (!e) > + return false; > + > + /* It is, or is a pointer to, a TU-local function or the object associated > + with a TU-local variable. */ > + tree object = NULL_TREE; > + if (TREE_CODE (e) == ADDR_EXPR) > + object = TREE_OPERAND (e, 0); > + else if (TREE_CODE (e) == PTRMEM_CST) > + object = PTRMEM_CST_MEMBER (e); > + else if (VAR_OR_FUNCTION_DECL_P (e)) > + object = e; > + > + if (object > + && VAR_OR_FUNCTION_DECL_P (object) > + && is_tu_local_entity (object)) > + { > + if (explain) > + { > + /* We've lost a lot of location information by the time we get here, > + so let's just do our best effort. */ > + auto loc = cp_expr_loc_or_loc (expr, DECL_SOURCE_LOCATION (decl)); > + if (VAR_P (object)) > + inform (loc, "%qD refers to TU-local object %qD", decl, object); > + else > + inform (loc, "%qD refers to TU-local function %qD", decl, object); > + is_tu_local_entity (object, true); > + } > + return true; > + } > + > + /* It is an object of class or array type and any of its subobjects or > + any of the objects or functions to which its non-static data members > + of reference type refer is TU-local and is usable in constant > + expressions. */ > + if (TREE_CODE (e) == CONSTRUCTOR && AGGREGATE_TYPE_P (TREE_TYPE (e))) > + for (auto& f : CONSTRUCTOR_ELTS (e)) > + if (is_tu_local_value (decl, f.value, explain)) > + return true; > + > + return false; > +} > + > /* DECL is a newly discovered dependency. Create the depset, if it > doesn't already exist. Add it to the worklist if so. > > @@ -13014,26 +13266,30 @@ depset::hash::make_dependency (tree decl, > entity_kind ek) > } > } > > - if (ek == EK_DECL > + if (!header_module_p () > && !dep->is_import () > - && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL > - && !(TREE_CODE (decl) == TEMPLATE_DECL > - && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))) > - { > - tree ctx = CP_DECL_CONTEXT (decl); > + && is_tu_local_entity (decl)) > + /* An internal decl. This is OK in a header unit. */ > + dep->set_flag_bit<DB_TU_LOCAL_BIT> (); > > - if (!TREE_PUBLIC (ctx)) > - /* Member of internal namespace. */ > - dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); > - else if (VAR_OR_FUNCTION_DECL_P (not_tmpl) > - && DECL_THIS_STATIC (not_tmpl)) > - { > - /* An internal decl. This is ok in a GM entity. */ > - if (!(header_module_p () > - || !DECL_LANG_SPECIFIC (not_tmpl) > - || !DECL_MODULE_PURVIEW_P (not_tmpl))) > - dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); > - } > + if (!header_module_p () > + && !dep->is_import () > + && VAR_P (decl) > + && (TREE_CONSTANT (decl) > + || (TYPE_REF_P (TREE_TYPE (decl)) > + && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))) > + && is_tu_local_value (decl, DECL_INITIAL (decl))) > + { > + /* A potentially-constant variable initialized to a TU-local > + value is not usable in constant expressions within other > + translation units. We can achieve this by simply not > + streaming the definition in such cases. */ > + dep->clear_flag_bit<DB_DEFN_BIT> (); > + > + if (DECL_DECLARED_CONSTEXPR_P (decl)) > + /* Also, a constexpr variable initialized to a TU-local > + value is an exposure. This is OK in a header unit. */ > + dep->set_flag_bit<DB_EXPOSURE_BIT> (); > } > } > > @@ -13059,8 +13315,8 @@ depset::hash::add_dependency (depset *dep) > gcc_checking_assert (current && !is_key_order ()); > current->deps.safe_push (dep); > > - if (dep->is_internal () && !current->is_internal ()) > - current->set_flag_bit<DB_REFS_INTERNAL_BIT> (); > + if (dep->is_tu_local ()) > + current->set_flag_bit<DB_EXPOSURE_BIT> (); > > if (current->get_entity_kind () == EK_USING > && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) > @@ -13168,13 +13424,9 @@ depset::hash::add_binding_entity (tree decl, > WMB_Flags flags, void *data_) > /* Ignore entities not within the module purview. */ > return false; > > - if (VAR_OR_FUNCTION_DECL_P (inner) > - && DECL_THIS_STATIC (inner)) > - { > - if (!header_module_p ()) > - /* Ignore internal-linkage entitites. */ > - return false; > - } > + if (!header_module_p () && data->hash->is_tu_local_entity (decl)) > + /* Ignore TU-local entitites. */ > + return false; > > if ((TREE_CODE (decl) == VAR_DECL > || TREE_CODE (decl) == TYPE_DECL) > @@ -13873,10 +14125,8 @@ bool > depset::hash::finalize_dependencies () > { > bool ok = true; > - depset::hash::iterator end (this->end ()); > - for (depset::hash::iterator iter (begin ()); iter != end; ++iter) > + for (depset *dep : *this) > { > - depset *dep = *iter; > if (dep->is_binding ()) > { > /* Keep the containing namespace dep first. */ > @@ -13889,23 +14139,41 @@ depset::hash::finalize_dependencies () > gcc_qsort (&dep->deps[1], dep->deps.length () - 1, > sizeof (dep->deps[1]), binding_cmp); > } > - else if (dep->refs_internal ()) > + else if (dep->is_exposure () && !dep->is_tu_local ()) > { > - for (unsigned ix = dep->deps.length (); ix--;) > + ok = false; > + bool explained = false; > + tree decl = dep->get_entity (); > + > + for (depset *rdep : dep->deps) > + if (!rdep->is_binding () && rdep->is_tu_local ()) > + { > + // FIXME:QOI Better location information? We're > + // losing, so it doesn't matter about efficiency > + tree exposed = rdep->get_entity (); > + auto_diagnostic_group d; > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qD exposes TU-local entity %qD", decl, exposed); > + bool informed = is_tu_local_entity (exposed, /*explain=*/true); > + gcc_checking_assert (informed); > + explained = true; > + break; > + } > + > + if (!explained && VAR_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl)) > { > - depset *rdep = dep->deps[ix]; > - if (rdep->is_internal ()) > - { > - // FIXME:QOI Better location information? We're > - // losing, so it doesn't matter about efficiency > - tree decl = dep->get_entity (); > - error_at (DECL_SOURCE_LOCATION (decl), > - "%q#D references internal linkage entity %q#D", > - decl, rdep->get_entity ()); > - break; > - } > + auto_diagnostic_group d; > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qD is declared %<constexpr%> and is initialized to " > + "a TU-local value", decl); > + bool informed = is_tu_local_value (decl, DECL_INITIAL (decl), > + /*explain=*/true); > + gcc_checking_assert (informed); > + explained = true; > } > - ok = false; > + > + /* We should have emitted an error above. */ > + gcc_checking_assert (explained); > } > } > > @@ -13929,7 +14197,9 @@ void > depset::tarjan::connect (depset *v) > { > gcc_checking_assert (v->is_binding () > - || !(v->is_unreached () || v->is_import ())); > + || !(v->is_tu_local () > + || v->is_unreached () > + || v->is_import ())); > > v->cluster = v->section = ++index; > stack.safe_push (v); > @@ -13939,7 +14209,8 @@ depset::tarjan::connect (depset *v) > { > depset *dep = v->deps[ix]; > > - if (dep->is_binding () || !dep->is_import ()) > + if (dep->is_binding () > + || !(dep->is_import () || dep->is_tu_local ())) > { > unsigned lwm = dep->cluster; > > @@ -14142,14 +14413,12 @@ depset::hash::connect () > tarjan connector (size ()); > vec<depset *> deps; > deps.create (size ()); > - iterator end (this->end ()); > - for (iterator iter (begin ()); iter != end; ++iter) > + for (depset *item : *this) > { > - depset *item = *iter; > - > entity_kind kind = item->get_entity_kind (); > if (kind == EK_BINDING > || !(kind == EK_REDIRECT > + || item->is_tu_local () > || item->is_unreached () > || item->is_import ())) > deps.quick_push (item); > @@ -18411,7 +18680,8 @@ module_state::write_begin (elf_out *to, cpp_reader > *reader, > note_defs = note_defs_table_t::create_ggc (1000); > #endif > > - /* Determine Strongy Connected Components. */ > + /* Determine Strongy Connected Components. This will also strip any > + unnecessary dependencies on imported or TU-local entities. */ > vec<depset *> sccs = table.connect (); > > vec_alloc (ool, modules->length ()); > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 4dd9474cf60..f75d3f53343 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -564,6 +564,8 @@ cp_debug_parser (FILE *file, cp_parser *parser) > parser->in_unbraced_export_declaration_p); > cp_debug_print_flag (file, "Parsing a declarator", > parser->in_declarator_p); > + cp_debug_print_flag (file, "Parsing an initializer", > + parser->in_initializer_p); > cp_debug_print_flag (file, "In template argument list", > parser->in_template_argument_list_p); > cp_debug_print_flag (file, "Parsing an iteration statement", > @@ -4455,6 +4457,9 @@ cp_parser_new (cp_lexer *lexer) > /* We are not processing a declarator. */ > parser->in_declarator_p = false; > > + /* We are not processing an initializer. */ > + parser->in_initializer_p = false; > + > /* We are not processing a template-argument-list. */ > parser->in_template_argument_list_p = false; > > @@ -11426,6 +11431,7 @@ cp_parser_lambda_expression (cp_parser* parser) > > record_lambda_scope (lambda_expr); > record_lambda_scope_discriminator (lambda_expr); > + TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p; > > /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ > determine_visibility (TYPE_NAME (type)); > @@ -26331,6 +26337,9 @@ cp_parser_initializer (cp_parser *parser, bool > *is_direct_init /*=nullptr*/, > if (non_constant_p) > *non_constant_p = false; > > + bool saved_in_initializer_p = parser->in_initializer_p; > + parser->in_initializer_p = true; > + > if (token->type == CPP_EQ) > { > /* Consume the `='. */ > @@ -26367,6 +26376,8 @@ cp_parser_initializer (cp_parser *parser, bool > *is_direct_init /*=nullptr*/, > if (!subexpression_p && check_for_bare_parameter_packs (init)) > init = error_mark_node; > > + parser->in_initializer_p = saved_in_initializer_p; > + > return init; > } > > @@ -27937,6 +27948,8 @@ cp_parser_class_head (cp_parser* parser, > } > else if (type == error_mark_node) > type = NULL_TREE; > + else > + TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p; > > if (type) > { > @@ -31367,6 +31380,8 @@ cp_parser_concept_definition (cp_parser *parser) > } > > processing_constraint_expression_sentinel parsing_constraint; > + parser->in_initializer_p = true; > + > tree init = cp_parser_constraint_expression (parser); > if (init == error_mark_node) > cp_parser_skip_to_end_of_statement (parser); > @@ -31375,6 +31390,7 @@ cp_parser_concept_definition (cp_parser *parser) > but continue as if it were. */ > cp_parser_consume_semicolon_at_end_of_statement (parser); > > + parser->in_initializer_p = false; > return finish_concept_definition (id, init, attrs); > } > > diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h > index 09b356e5e73..ee8c4f9001e 100644 > --- a/gcc/cp/parser.h > +++ b/gcc/cp/parser.h > @@ -323,6 +323,9 @@ struct GTY(()) cp_parser { > direct-declarator. */ > bool in_declarator_p; > > + /* TRUE if we are parsing an initializer. */ > + bool in_initializer_p; > + > /* TRUE if we are presently parsing a template-argument-list. */ > bool in_template_argument_list_p; > > diff --git a/gcc/testsuite/g++.dg/modules/block-decl-2.C > b/gcc/testsuite/g++.dg/modules/block-decl-2.C > index d491a18dfb1..104a98ab052 100644 > --- a/gcc/testsuite/g++.dg/modules/block-decl-2.C > +++ b/gcc/testsuite/g++.dg/modules/block-decl-2.C > @@ -11,7 +11,7 @@ export extern "C++" auto foo() { > struct X { > // `foo` is not attached to a named module, and as such > // `X::f` should be implicitly `inline` here > - void f() { // { dg-error "references internal linkage entity" } > + void f() { // { dg-error "exposes TU-local entity" } > internal(); > } > }; > diff --git a/gcc/testsuite/g++.dg/modules/internal-1.C > b/gcc/testsuite/g++.dg/modules/internal-1.C > index 9f7299a5fc7..3ed74c9cc42 100644 > --- a/gcc/testsuite/g++.dg/modules/internal-1.C > +++ b/gcc/testsuite/g++.dg/modules/internal-1.C > @@ -3,13 +3,10 @@ > export module frob; > // { dg-module-cmi !frob } > > -namespace { > -// We shouldn't be complaining about members of internal linkage > -// entities > -class X // { dg-bogus "internal linkage" "" { xfail *-*-* } } > -{ // { dg-bogus "internal linkage" "" { xfail *-*-* } } > -}; > - > +namespace > +{ > + // We shouldn't be complaining about members of internal linkage entities > + class X {}; > } > > static int frob () > @@ -17,5 +14,5 @@ static int frob () > return 1; > } > > -export int f (int = frob ()); // { dg-error "references internal linkage" } > -int goof (X &); // { dg-error "references internal linkage" } > +export int f (int = frob ()); // { dg-error "exposes TU-local entity" } > +int goof (X &); // { dg-error "exposes TU-local entity" } > diff --git a/gcc/testsuite/g++.dg/modules/internal-3.C > b/gcc/testsuite/g++.dg/modules/internal-3.C > new file mode 100644 > index 00000000000..91aae32783f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/internal-3.C > @@ -0,0 +1,18 @@ > +// { dg-additional-options "-fmodules-ts -Wno-global-module" } > +// { dg-module-cmi !M } > +// TU-local entities in the GMF can be exposed. > + > +module; > + > +static inline void foo() {} > + > +export module M; > + > +inline void bar() { // { dg-error "exposes TU-local entity" } > + foo(); > +} > + > +// OK > +void qux() { > + foo(); > +} > diff --git a/gcc/testsuite/g++.dg/modules/internal-4.C > b/gcc/testsuite/g++.dg/modules/internal-4.C > new file mode 100644 > index 00000000000..0c9a790d379 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/internal-4.C > @@ -0,0 +1,112 @@ > +// { dg-additional-options "-fmodules-ts" } > +// { dg-module-cmi !bad } > +// Test for determining various kinds of entities being marked TU-local > + > +export module bad; > + > + > +// A type, function variable, or template with internal linkage > +namespace { > + struct internal_t { int m; }; > + enum internal_e {}; > + void internal_f() {} > + int internal_v; > + template <typename T> void internal_x() {} > +} > + > +inline void expose_type() { // { dg-error "exposes TU-local entity" } > + internal_t x; > +} > +inline void expose_func() { // { dg-error "exposes TU-local entity" } > + internal_f(); > +} > +inline void expose_var() { // { dg-error "exposes TU-local entity" } > + int* p = &internal_v; > +} > +inline void expose_tmpl() { // { dg-error "exposes TU-local entity" } > + internal_x<int>(); > +} > + > + > +// Does not have a name with linkage and is declared, or introduced by > +// a lambda-expression, within the definition of a TU-local entity > +static auto get_local_ok() { > + return 0; > +} > +static auto get_local_type() { > + struct no_linkage {}; > + return no_linkage(); > +} > +static auto get_local_lambda() { > + return []{}; > +} > +using T = decltype(get_local_ok()); // OK > +using U = decltype(get_local_type()); // { dg-error "exposes TU-local > entity" } > +using V = decltype(get_local_lambda()); // { dg-error "exposes TU-local > entity" } > + > +static auto internal_lambda = []{ internal_f(); }; // OK > +auto expose_lambda = internal_lambda; // { dg-error "exposes TU-local > entity" } > + > +int not_in_tu_local > + = ([]{ internal_f(); }(), // { dg-error "exposes TU-local entity" } > + 0); > + > + > +// A type with no name that is defined outside a class-specifier, function > +// body, or initializer > + > +struct {} no_name; // { dg-error "exposes TU-local entity" } > +enum {} e; // { dg-error "exposes TU-local entity" } > +using not_an_initializer = class {}; // { dg-error "exposes TU-local > entity" } > + > +class in_class_specifier { struct {} x; }; // OK > +void in_function_body() { struct {} x; } // OK > +auto in_initializer = []{}; // OK > + > +#if __cplusplus >= 202002L > +decltype([]{}) d_lambda; // { dg-error "exposes TU-local entity" "" { > target c++20 } } > + > +template <typename T> > +concept in_constraint_expression = requires { > + // Strictly by the standard this is currently ill-formed > + // (this is a constraint-expression not an initializer) > + // but I don't think that is intended. > + []{}; // OK? > +}; > +#endif > + > +// (But consider unnamed types with names for linkage purposes as having > names) > +typedef struct {} no_name_typedef_t; > +no_name_typedef_t linkage_name_struct; // OK > + > +enum { enum_name } linkage_name_enum; // OK > + > + > +// Specialisation of a TU-local template > +template <typename T> static void f(T) {} > +template <> void f(int) {} // OK > +inline void f_use(int x) { // { dg-error "exposes TU-local entity" } > + f(x); > +} > + > + > +// Specialisation of a template with any TU-local argument > +template <typename T> void g(T) {} > +template <> void g(internal_t) { internal_f(); } // OK > +template <> void g(internal_e) { internal_f(); } // OK > +template <> void g(decltype(no_name)) { internal_f(); } // OK > +template <> void g(decltype(get_local_lambda())) { internal_f(); } // OK > + > +template <auto X> struct h {}; > +template struct h<&internal_v>; > +template <> struct h<&internal_f> { internal_t x; }; // OK > +template <> struct h<&internal_t::m> { void foo() { internal_f(); } }; // OK > + > + > +// TODO: I can't come up with testcases for these that aren't already covered > +// by one of the above cases: > +// > +// - A type with no name introduced by a defining-type-specifier that is > +// used to declare only TU-local entities > +// - A specialisation of a template whose (possibly instantiated) declaration > +// is an exposure > diff --git a/gcc/testsuite/g++.dg/modules/linkage-2.C > b/gcc/testsuite/g++.dg/modules/linkage-2.C > index 4b20411572c..97421bfad8e 100644 > --- a/gcc/testsuite/g++.dg/modules/linkage-2.C > +++ b/gcc/testsuite/g++.dg/modules/linkage-2.C > @@ -25,6 +25,5 @@ export void use() { > > // Additionally, unnamed types have no linkage but are also TU-local, and > thus > // cannot be exposed in a module interface unit. The non-TU-local entity 's' > -// here is an exposure of this type, so this should be an error; we don't yet > -// implement this checking however. > -struct {} s; // { dg-error "TU-local" "" { xfail *-*-* } } > +// here is an exposure of this type. > +struct {} s; // { dg-error "exposes TU-local entity" } > -- > 2.46.0 >