@@ -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) \
@@ -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 *);
@@ -5404,6 +5407,7 @@ trees_out::core_bools (tree t, bits_out& bits)
WB (t->type_common.lang_flag_4);
WB (t->type_common.lang_flag_5);
WB (t->type_common.lang_flag_6);
+ WB (t->type_common.lang_flag_7);
WB (t->type_common.typeless_storage);
}
@@ -5605,6 +5609,7 @@ trees_in::core_bools (tree t, bits_in& bits)
RB (t->type_common.lang_flag_4);
RB (t->type_common.lang_flag_5);
RB (t->type_common.lang_flag_6);
+ RB (t->type_common.lang_flag_7);
RB (t->type_common.typeless_storage);
}
@@ -12886,6 +12891,246 @@ 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
+ /* But although weakrefs are marked static, don't consider them
+ to be TU-local. */
+ && !lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
+ {
+ 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.
@@ -12992,6 +13237,7 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
if (ek != EK_USING)
{
tree not_tmpl = STRIP_TEMPLATE (decl);
+ bool imported_from_module_p = false;
if (DECL_LANG_SPECIFIC (not_tmpl)
&& DECL_MODULE_IMPORT_P (not_tmpl))
@@ -13007,28 +13253,36 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
dep->cluster = index - from->entity_lwm;
dep->section = from->remap;
dep->set_flag_bit<DB_IMPORTED_BIT> ();
+
+ if (!from->is_header ())
+ imported_from_module_p = true;
}
}
- if (ek == EK_DECL
- && !dep->is_import ()
- && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL
- && !(TREE_CODE (decl) == TEMPLATE_DECL
- && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)))
+ /* Check for TU-local entities. This is unnecessary in header
+ units because we can export internal-linkage decls, and
+ no declarations are exposures. Similarly, if the decl was
+ imported from a non-header module we know it cannot have
+ been TU-local. */
+ if (!header_module_p () && !imported_from_module_p)
{
- tree ctx = CP_DECL_CONTEXT (decl);
+ if (is_tu_local_entity (decl))
+ 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))
+ if (VAR_P (decl)
+ && decl_maybe_constant_var_p (decl)
+ && is_tu_local_value (decl, DECL_INITIAL (decl)))
{
- /* 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> ();
+ /* 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. */
+ dep->set_flag_bit<DB_EXPOSURE_BIT> ();
}
}
}
@@ -13055,8 +13309,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 ())
@@ -13164,13 +13418,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)
@@ -13869,10 +14119,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. */
@@ -13885,23 +14133,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);
}
}
@@ -13925,7 +14191,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);
@@ -13935,7 +14203,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;
@@ -14138,14 +14407,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);
@@ -18407,7 +18674,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 ());
@@ -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));
@@ -26347,6 +26353,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 `='. */
@@ -26383,6 +26392,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;
}
@@ -27953,6 +27964,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)
{
@@ -31383,6 +31396,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);
@@ -31391,6 +31406,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);
}
@@ -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;
@@ -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();
}
};
@@ -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" }
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -0,0 +1,4 @@
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi {} }
+
+static void header_f() {}
new file mode 100644
@@ -0,0 +1,124 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi !bad }
+// Test for determining various kinds of entities being marked TU-local
+
+export module bad;
+import "internal-4_a.H";
+
+
+// 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>();
+}
+inline void expose_header_decl() { // { dg-error "exposes TU-local entity" }
+ header_f();
+}
+
+// But we don't consider a weakref as being TU-local, despite being
+// marked static; this is to support uses of weakrefs in header files
+// (such as via the standard library).
+static void weakref() __attribute__((weakref("target")));
+inline void expose_weakref() {
+ weakref();
+}
+
+
+// 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
@@ -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" }