https://gcc.gnu.org/g:9016c5ac94c55714d001309ef640710f9d512254

commit r15-6378-g9016c5ac94c55714d001309ef640710f9d512254
Author: Nathaniel Shead <nathanielosh...@gmail.com>
Date:   Tue Oct 8 20:50:38 2024 +1100

    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::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.
    
    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 <nathanielosh...@gmail.com>
    Reviewed-by: Jason Merrill <ja...@redhat.com>

Diff:
---
 gcc/cp/module.cc                            | 382 +++++++++++++++++++++++-----
 gcc/cp/name-lookup.cc                       |   2 +-
 gcc/cp/tree.cc                              |  10 +-
 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_a.H |   4 +
 gcc/testsuite/g++.dg/modules/internal-4_b.C | 128 ++++++++++
 gcc/testsuite/g++.dg/modules/linkage-2.C    |   5 +-
 9 files changed, 491 insertions(+), 75 deletions(-)

diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index f2a4fb16c078..09158b0f0e62 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -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--;)
+         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);
        }
     }
 
@@ -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 ());
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 0da0441e3a0f..35e7dc81377a 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -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);
 
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 97ce1f66d2fd..1eef600c79a4 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -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;
diff --git a/gcc/testsuite/g++.dg/modules/block-decl-2.C 
b/gcc/testsuite/g++.dg/modules/block-decl-2.C
index d491a18dfb12..104a98ab0524 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 9f7299a5fc7b..3ed74c9cc423 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 000000000000..91aae32783f3
--- /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_a.H 
b/gcc/testsuite/g++.dg/modules/internal-4_a.H
new file mode 100644
index 000000000000..04dfac8c936e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-4_a.H
@@ -0,0 +1,4 @@
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi {} }
+
+static void header_f() {}
diff --git a/gcc/testsuite/g++.dg/modules/internal-4_b.C 
b/gcc/testsuite/g++.dg/modules/internal-4_b.C
new file mode 100644
index 000000000000..86bec294fc83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-4_b.C
@@ -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
diff --git a/gcc/testsuite/g++.dg/modules/linkage-2.C 
b/gcc/testsuite/g++.dg/modules/linkage-2.C
index 4b20411572c0..97421bfad8e7 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" }

Reply via email to