c++/modules: Detect exposures of TU-local entities

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.

TU-local lambdas are also not yet properly implemented, due to other
bugs with regards to LAMBDA_TYPE_EXTRA_SCOPE not being set in all cases
that it probably should be (see also PR c++/116568).  We can revisit
this once that issue has been fixed.

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:

	* tree.cc (decl_linkage): Treat DECL_SELF_REFERENCE_P like
	DECL_IMPLICIT_TYPEDEF_P.
	* name-lookup.cc (do_namespace_alias): Fix linkage.
	* module.cc (DB_IS_INTERNAL_BIT): Rename to...
	(DB_TU_LOCAL_BIT): ...this.
	(DB_REFS_INTERNAL_BIT): Rename to...
	(DB_EXPOSURE_BIT): ...this.
	(depset:#️⃣:is_internal): Rename to...
	(depset:#️⃣:is_tu_local): ...this.
	(depset:#️⃣:refs_internal): Rename to...
	(depset:#️⃣:is_exposure): ...this.
	(depset:#️⃣:is_tu_local_entity): New function.
	(depset:#️⃣:has_tu_local_tmpl_arg): New function.
	(depset:#️⃣:is_tu_local_value): New function.
	(depset:#️⃣:make_dependency): Check for TU-local entities.
	(depset:#️⃣:add_dependency): Make current an exposure
	whenever it references a TU-local entity.
	(depset:#️⃣:add_binding_entity): Don't create bindings for
	any TU-local entity.
	(depset:#️⃣: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:#️⃣:connect): Likewise.

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_a.H: New test.
	* g++.dg/modules/internal-4_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
Nathaniel Shead 2024-10-08 20:50:38 +11:00
parent b11e85adbf
commit 9016c5ac94
9 changed files with 492 additions and 76 deletions

View file

@ -2352,10 +2352,8 @@ private:
DB_KIND_BITS = EK_BITS,
DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
DB_IS_PENDING_BIT, /* Is a maybe-pending entity. */
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_MAYBE_RECURSIVE_BIT, /* An entity maybe in a recursive cluster. */
@ -2441,13 +2439,13 @@ public:
&& get_flag_bit<DB_IS_PENDING_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
{
@ -2616,6 +2614,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 *);
@ -13041,6 +13044,248 @@ 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.
Neither are the built-in declarations of 'int' and such. */
if (TREE_CODE (decl) == TYPE_DECL
&& !DECL_SELF_REFERENCE_P (decl)
&& !DECL_IMPLICIT_TYPEDEF_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. */
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 (!DECL_CLASS_SCOPE_P (main_decl)
&& !decl_function_context (main_decl)
/* FIXME: Lambdas defined outside initializers. We'll need to more
thoroughly set LAMBDA_TYPE_EXTRA_SCOPE to check this. */
&& !LAMBDA_TYPE_P (type))
{
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.
@ -13147,6 +13392,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))
@ -13162,28 +13408,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> ();
}
}
@ -13222,8 +13476,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 ())
@ -13342,13 +13596,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)
@ -14044,10 +14294,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. */
@ -14060,23 +14308,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--;)
{
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;
}
}
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))
{
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;
}
/* We should have emitted an error above. */
gcc_checking_assert (explained);
}
}
@ -14100,7 +14366,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);
@ -14110,7 +14378,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;
@ -14326,14 +14595,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);
@ -18612,7 +18879,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 Strongly 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 ());

View file

@ -6620,7 +6620,7 @@ do_namespace_alias (tree alias, tree name_space)
DECL_NAMESPACE_ALIAS (alias) = name_space;
DECL_EXTERNAL (alias) = 1;
DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
TREE_PUBLIC (alias) = TREE_PUBLIC (DECL_CONTEXT (alias));
TREE_PUBLIC (alias) = TREE_PUBLIC (CP_DECL_CONTEXT (alias));
alias = pushdecl (alias);

View file

@ -5935,14 +5935,16 @@ decl_linkage (tree decl)
linkage first, and then transform that into a concrete
implementation. */
/* An explicit type alias has no linkage. */
/* An explicit type alias has no linkage. Nor do the built-in declarations
of 'int' and such. */
if (TREE_CODE (decl) == TYPE_DECL
&& !DECL_IMPLICIT_TYPEDEF_P (decl)
&& !DECL_SELF_REFERENCE_P (decl))
&& !DECL_IMPLICIT_TYPEDEF_P (decl))
{
/* But this could be a typedef name for linkage purposes, in which
case we're interested in the linkage of the main decl. */
if (decl == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl))))
if (decl == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl)))
/* Likewise for the injected-class-name. */
|| DECL_SELF_REFERENCE_P (decl))
decl = TYPE_MAIN_DECL (TREE_TYPE (decl));
else
return lk_none;

View file

@ -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();
}
};

View file

@ -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" }

View file

@ -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();
}

View file

@ -0,0 +1,4 @@
// { dg-additional-options "-fmodule-header" }
// { dg-module-cmi {} }
static void header_f() {}

View file

@ -0,0 +1,128 @@
// { 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() {}
namespace internal_ns {}
}
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();
}
// We additionally consider a namespace with internal linkage as TU-local
namespace expose_ns = internal_ns; // { dg-error "exposes TU-local entity" }
// 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" "" { xfail *-*-* } }
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.
[]{}; // { dg-bogus "exposes TU-local entity" }
};
#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

View file

@ -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" }