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 <[email protected]>
> ---
> 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
>