depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/)
{
gcc_checking_assert (DECL_P (decl));
+ location_t loc = DECL_SOURCE_LOCATION (decl);
+ tree type = TREE_TYPE (decl);
+
+ /* Only types, functions, variables, and template (specialisations)
+ can be TU-local. */
+ if (TREE_CODE (decl) != TYPE_DECL
+ && TREE_CODE (decl) != FUNCTION_DECL
+ && TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != TEMPLATE_DECL)
+ return false;
- /* An explicit type alias is not an entity, and so is never TU-local.
- Neither are the built-in declarations of 'int' and such. */
+ /* An explicit type alias is not an entity; we don't want to stream
+ such aliases if they refer to TU-local entities, so propagate this
+ from the original type. The built-in declarations of 'int' and such
+ are never TU-local. */
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);
+ {
+ tree orig = DECL_ORIGINAL_TYPE (decl);
+ if (orig && TYPE_NAME (orig))
+ {
+ if (explain)
+ inform (loc, "%qD is an alias of TU-local type %qT", decl, orig);
+ return is_tu_local_entity (TYPE_NAME (orig), explain);
+ }
+ else
+ return false;
+ }
/* Check specializations first for slightly better explanations. */
int use_tpl = -1;
depset *b = spaces[ix];
tree ns = b->get_entity ();
+ /* This could be an anonymous namespace even for a named module,
+ since we can still emit no-linkage decls. */
gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
- gcc_checking_assert (TREE_PUBLIC (ns) || header_module_p ());
unsigned flags = 0;
if (TREE_PUBLIC (ns))
/* An internal-linkage declaration cannot be generally be exported.
But it's OK to export any declaration from a header unit, including
internal linkage declarations. */
- if (!header_module_p ()
- && DECL_MODULE_EXPORT_P (decl)
- && decl_linkage (decl) == lk_internal)
+ if (!header_module_p () && DECL_MODULE_EXPORT_P (decl))
{
- error_at (DECL_SOURCE_LOCATION (decl),
- "exporting declaration %qD with internal linkage", decl);
- DECL_MODULE_EXPORT_P (decl) = false;
+ /* Let's additionally treat any exported declaration within an
+ internal namespace as exporting a declaration with internal
+ linkage, as this would also implicitly export the internal
+ linkage namespace. */
+ if (decl_internal_context_p (decl))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "exporting declaration %qD declared in unnamed namespace",
+ decl);
+ DECL_MODULE_EXPORT_P (decl) = false;
+ }
+ else if (decl_linkage (decl) == lk_internal)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "exporting declaration %qD with internal linkage", decl);
+ DECL_MODULE_EXPORT_P (decl) = false;
+ }
}
}
#endif
namespace {
- export int y = 456; // { dg-error "internal linkage" }
- export void h(); // { dg-error "internal linkage" }
- export void i() {} // { dg-error "internal linkage" }
- export template <typename T> void v(); // { dg-error "internal linkage" }
- export template <typename T> void w() {} // { dg-error "internal linkage" }
- export auto [e] = S{}; // { dg-error "internal linkage" }
+ export int y = 456; // { dg-error "exporting" }
+ export void h(); // { dg-error "exporting" }
+ export void i() {} // { dg-error "exporting" }
+ export template <typename T> void v(); // { dg-error "exporting" }
+ export template <typename T> void w() {} // { dg-error "exporting" }
+ export auto [e] = S{}; // { dg-error "exporting" }
- export namespace ns {} // { dg-error "internal linkage" }
- export namespace alias = global; // { dg-error "internal linkage" }
+ export namespace ns {} // { dg-error "exporting" }
+ export namespace alias = global; // { dg-error "exporting" }
- export struct A {}; // { dg-error "internal linkage" }
- export template <typename T> struct B {}; // { dg-error "internal linkage" }
+ export struct A {}; // { dg-error "exporting" }
+ export template <typename T> struct B {}; // { dg-error "exporting" }
- export enum E {}; // { dg-error "internal linkage" }
- export enum class F {}; // { dg-error "internal linkage" }
+ export enum E {}; // { dg-error "exporting" }
+ export enum class F {}; // { dg-error "exporting" }
- export template <typename T> using U = int; // { dg-error "internal linkage" }
+ export template <typename T> using U = int; // { dg-error "exporting" }
#if __cplusplus >= 202002L
- export template <typename T> concept C = true; // { dg-error "internal linkage" "" { target c++20 } }
+ export template <typename T> concept C = true; // { dg-error "exporting" "" { target c++20 } }
#endif
+
+ // Also complain about exporting no-linkage decls in an unnamed namespace
+ export typedef int T; // { dg-error "exporting" }
+ export typedef struct {} *PC; // { dg-error "exporting" }
+ export using V = int; // { dg-error "exporting" }
}
export namespace {} // { dg-error "exporting unnamed namespace" }
header_f();
}
-// We additionally consider a namespace with internal linkage as TU-local
-namespace expose_ns = internal_ns; // { dg-error "exposes TU-local entity" }
+// A namespace with internal linkage is not inherently an exposure.
+namespace expose_ns = internal_ns; // { dg-bogus "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
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" }
+using T = decltype(get_local_ok());
+T* expose_t; // OK
+using U = decltype(get_local_type());
+U* expose_u; // { dg-error "exposes TU-local entity" }
+using V = decltype(get_local_lambda());
+V* expose_v; // { dg-error "exposes TU-local entity" }
static auto internal_lambda = []{ internal_f(); }; // OK
auto expose_lambda = internal_lambda; // { dg-error "exposes TU-local entity" }
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" }
+using not_an_initializer = class {};
+not_an_initializer expose_nai; // { dg-error "exposes TU-local entity" }
class in_class_specifier { struct {} x; }; // OK
void in_function_body() { struct {} x; } // OK
#if __cplusplus >= 202002L
decltype([]{}) d_lambda; // { dg-error "exposes TU-local entity" "" { target c++20 } }
-using alias_lambda = decltype([]{}); // { dg-error "exposes TU-local entity" "" { target c++20 } }
+using alias_lambda = decltype([]{});
+alias_lambda expose_lam; // { dg-error "exposes TU-local entity" "" { target c++20 } }
template <typename T>
concept in_constraint_expression = requires {
--- /dev/null
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+
+export module M;
+
+namespace {
+ using A = int;
+ typedef int B;
+
+ struct Internal {};
+ using C = Internal;
+ typedef Internal D;
+}
--- /dev/null
+// { dg-additional-options "-fmodules" }
+
+module M;
+
+A x;
+B y;
+
+// Aliases that refer to TU-local entities should not be visible.
+C c; // { dg-error "does not name a type" }
+D d; // { dg-error "does not name a type" }
--- /dev/null
+// { dg-additional-options "-fmodules" }
+// The different declarations in the anonymous namespace shouldn't clash with
+// those in M.
+
+namespace {
+ using A = double;
+ typedef double B;
+ using C = double;
+ typedef double D;
+}
+import M;
+int main() {
+ A a = 1.0;
+ B b = 2.0;
+ C c = 3.0;
+ D d = 4.0;
+}