Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

-- >8 --

This finishes the reworking of ADL handling for modules for PR117658.
[basic.link] p18 says that we should diagnose any references to a
TU-local entity from a different TU; with our fixed handling of ADL this
is now possible to occur.

To do this we need to generate fake bindings for these decls on
stream-out so that importers know that we have a visible name in this
namespace.  We don't actually need (or want) to provide full DECLs
though, as that could potentially involve streaming other TU-local
entities, so we just build a TU_LOCAL_ENTITY for the decl on receipt.
The patch also uses 'decl_as_string' to give a more descriptive name for
these decls when erroring.

We also need somewhere to store these decls.  We only actually need the
decls for diagnostics; for correctness we only need to know whether any
such decls existed, so to not mess with the existing packing of bindings
or usages of the OVERLOADs this patch adds a new map to the binding
vector that can be looked up when diagnostics need to be generated.

Finally, as specified this diagnostic is a pretty broad hammer, as any
internal-linkage purview function will stop ADL in exported templates
from working with that name.  So this patch just makes it a pedwarn and
provides an option to disable if needed.

        PR c++/117658

gcc/c-family/ChangeLog:

        * c.opt: New flag '-Wexternal-tu-local'.
        * c.opt.urls: Regenerate.

gcc/cp/ChangeLog:

        * cp-tree.h (TU_LOCAL_ENTITY_NAME): Clarify meaning.
        * module.cc (depset::entity_kind): New enumerator, assert that
        we have enough bits reserved.
        (depset::disc_bits): Assert the discriminator has enough bits.
        (depset::entity_kind_name): Add 'tu-local' case, assert we
        have an entry for all relevant entry_kinds.
        (name_for_tu_local_decl): New function.
        (trees_out::tree_node): Use it.
        (depset::hash::make_dependency): Validate EK_TU_LOCAL.
        (depset::hash::add_binding_entity): Generate bindings for
        internal purview functions.
        (enum ct_bind_flags): New enum for TU-local decls.
        (depset::hash::find_dependencies): Handle EK_TU_LOCAL entities.
        (binding_cmp): Likewise.
        (sort_cluster): Likewise.
        (module_state::write_cluster): Likewise.
        (module_state::read_cluster): Likewise.
        * name-lookup.cc (append_imported_binding_slot): Propagate
        internal decl list when growing binding vector.
        (name_lookup::adl_namespace_fns): Diagnose if naming a TU-local
        entity from a different TU.
        (set_module_binding): Include any internal decls in binding.
        * name-lookup.h (struct module_tree_map_traits): New type.
        (struct tree_binding_vec): Add member 'internal_decls'.
        (BINDING_VECTOR_INTERNAL_DECLS): New getter.
        (MODULE_BINDING_INTERNAL_DECLS_P): New flag.
        (set_module_binding): Add parameter.

gcc/doc/ChangeLog:

        * invoke.texi: Document '-Wno-external-tu-local'.

gcc/testsuite/ChangeLog:

        * g++.dg/modules/adl-6_c.C: Adjust diagnostics.
        * g++.dg/modules/internal-14_c.C: Likewise.
        * g++.dg/modules/internal-15_a.C: New test.
        * g++.dg/modules/internal-15_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
---
 gcc/c-family/c.opt                           |   4 +
 gcc/c-family/c.opt.urls                      |   3 +
 gcc/cp/cp-tree.h                             |   3 +-
 gcc/cp/module.cc                             | 112 ++++++++++++++++---
 gcc/cp/name-lookup.cc                        |  58 ++++++++--
 gcc/cp/name-lookup.h                         |  16 ++-
 gcc/doc/invoke.texi                          |  28 ++++-
 gcc/testsuite/g++.dg/modules/adl-6_c.C       |   5 +-
 gcc/testsuite/g++.dg/modules/internal-14_c.C |   2 +-
 gcc/testsuite/g++.dg/modules/internal-15_a.C |  28 +++++
 gcc/testsuite/g++.dg/modules/internal-15_b.C |  13 +++
 11 files changed, 241 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-15_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-15_b.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 3f5e2f0874d..9e0e898a356 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -761,6 +761,10 @@ Wexpansion-to-defined
 C ObjC C++ ObjC++ CPP(warn_expansion_to_defined) 
CppReason(CPP_W_EXPANSION_TO_DEFINED) Var(cpp_warn_expansion_to_defined) 
Init(0) Warning EnabledBy(Wextra || Wpedantic)
 Warn if \"defined\" is used outside #if.
 
+Wexternal-tu-local
+C++ ObjC++ Var(warn_tu_local) Warning Init(1)
+Warn about naming a TU-local entity declared in another translation unit.
+
 Wextra
 C ObjC C++ ObjC++ Warning
 ; in common.opt
diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls
index e09d51d8afb..1d3f27af228 100644
--- a/gcc/c-family/c.opt.urls
+++ b/gcc/c-family/c.opt.urls
@@ -376,6 +376,9 @@ 
UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexceptions)
 Wexpansion-to-defined
 UrlSuffix(gcc/Warning-Options.html#index-Wexpansion-to-defined)
 
+Wexternal-tu-local
+UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexternal-tu-local)
+
 Wextra
 UrlSuffix(gcc/Warning-Options.html#index-Wextra) 
LangUrlSuffix_D(gdc/Warnings.html#index-Wextra) 
LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Wextra)
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d531bcd833b..ed46203662b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1856,7 +1856,8 @@ struct GTY(()) tree_tu_local_entity {
   location_t loc;
 };
 
-/* The name of a translation-unit-local entity.  */
+/* The human-readable name of a translation-unit-local entity as
+   an IDENTIFIER_NODE.  */
 #define TU_LOCAL_ENTITY_NAME(NODE) \
   (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name)
 
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index d5c3a83c728..5bc19a5e3f7 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -2341,6 +2341,7 @@ public:
     EK_PARTIAL,                /* A partial specialization.  */
     EK_USING,          /* A using declaration (at namespace scope).  */
     EK_NAMESPACE,      /* A namespace.  */
+    EK_TU_LOCAL,       /* A TU-local decl for ADL.  */
     EK_REDIRECT,       /* Redirect to a template_decl.  */
     EK_EXPLICIT_HWM,
     EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded.  */
@@ -2351,6 +2352,8 @@ public:
 
     EK_BITS = 3                /* Only need to encode below EK_EXPLICIT_HWM.  
*/
   };
+  static_assert (EK_EXPLICIT_HWM < (1u << EK_BITS),
+                "not enough bits reserved for entity_kind");
 
 private:
   /* Placement of bit fields in discriminator.  */
@@ -2375,7 +2378,10 @@ private:
        awkward.  */
     DB_TYPE_SPEC_BIT,          /* Specialization in the type table.  */
     DB_FRIEND_SPEC_BIT,                /* An instantiated template friend.  */
+    DB_HWM,
   };
+  static_assert (DB_HWM <= sizeof(discriminator) * CHAR_BIT,
+                "not enough bits in discriminator");
 
 public:
   /* The first slot is special for EK_SPECIALIZATIONS it is a
@@ -2700,7 +2706,9 @@ depset::entity_kind_name () const
   /* Same order as entity_kind.  */
   static const char *const names[] =
     {"decl", "specialization", "partial", "using",
-     "namespace", "redirect", "binding"};
+     "namespace", "tu-local", "redirect", "binding"};
+  static_assert (ARRAY_SIZE (names) == EK_EXPLICIT_HWM + 1,
+                "names must have an entry for every explicit entity_kind");
   entity_kind kind = get_entity_kind ();
   gcc_checking_assert (kind < ARRAY_SIZE (names));
   return names[kind];
@@ -9980,6 +9988,16 @@ trees_out::find_tu_local_decl (tree t)
   return cp_walk_tree_without_duplicates (&t, walker, this);
 }
 
+/* Get the name for TU-local decl T to be used in diagnostics.  */
+
+static tree
+name_for_tu_local_decl (tree t)
+{
+  int flags = (TFF_SCOPE | TFF_DECL_SPECIFIERS);
+  const char *str = decl_as_string (t, flags);
+  return get_identifier (str);
+}
+
 /* Stream out tree node T.  We automatically create local back
    references, which is essentially a single pass lisp
    self-referential structure pretty-printer.  */
@@ -10024,8 +10042,7 @@ trees_out::tree_node (tree t)
                && dump ("Writing TU-local entity:%d %C:%N",
                         tag, TREE_CODE (t), t);
            }
-         /* TODO: Get a more descriptive name?  */
-         tree_node (DECL_NAME (local_decl));
+         tree_node (name_for_tu_local_decl (local_decl));
          if (state)
            state->write_location (*this, DECL_SOURCE_LOCATION (local_decl));
          goto done;
@@ -14187,6 +14204,8 @@ depset::hash::make_dependency (tree decl, entity_kind 
ek)
   gcc_checking_assert (!is_key_order ());
   if (ek == EK_USING)
     gcc_checking_assert (TREE_CODE (decl) == OVERLOAD);
+  if (ek == EK_TU_LOCAL)
+    gcc_checking_assert (DECL_DECLARES_FUNCTION_P (decl));
 
   if (TREE_CODE (decl) == TEMPLATE_DECL)
     /* The template should have copied these from its result decl.  */
@@ -14387,7 +14406,8 @@ depset::hash::make_dependency (tree decl, entity_kind 
ek)
   dump (dumper::DEPEND)
     && dump ("%s on %s %C:%N found",
             ek == EK_REDIRECT ? "Redirect"
-            : for_binding ? "Binding" : "Dependency",
+            : (for_binding || ek == EK_TU_LOCAL) ? "Binding"
+            : "Dependency",
             dep->entity_kind_name (), TREE_CODE (decl), decl);
 
   return dep;
@@ -14558,9 +14578,21 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags 
flags, void *data_)
           than trying to clear out bindings after the fact.  */
        return false;
 
+      bool internal_decl = false;
       if (!header_module_p () && data->hash->is_tu_local_entity (decl))
-       /* Ignore TU-local entitites.  */
-       return false;
+       {
+         /* A TU-local entity.  For ADL we still need to create bindings
+            for internal-linkage functions attached to a named module.  */
+         if (DECL_DECLARES_FUNCTION_P (inner)
+             && DECL_LANG_SPECIFIC (inner)
+             && DECL_MODULE_ATTACH_P (inner))
+           {
+             gcc_checking_assert (!DECL_MODULE_EXPORT_P (inner));
+             internal_decl = true;
+           }
+         else
+           return false;
+       }
 
       if ((TREE_CODE (decl) == VAR_DECL
           || TREE_CODE (decl) == TYPE_DECL)
@@ -14655,8 +14687,13 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags 
flags, void *data_)
            OVL_EXPORT_P (decl) = true;
        }
 
-      depset *dep = data->hash->make_dependency
-       (decl, flags & WMB_Using ? EK_USING : EK_FOR_BINDING);
+      entity_kind ek = EK_FOR_BINDING;
+      if (internal_decl)
+       ek = EK_TU_LOCAL;
+      else if (flags & WMB_Using)
+       ek = EK_USING;
+
+      depset *dep = data->hash->make_dependency (decl, ek);
       if (flags & WMB_Hidden)
        dep->set_hidden_binding ();
       data->binding->deps.safe_push (dep);
@@ -15107,9 +15144,12 @@ depset::hash::find_dependencies (module_state *module)
              walker.begin ();
              if (current->get_entity_kind () == EK_USING)
                walker.tree_node (OVL_FUNCTION (decl));
+             else if (current->get_entity_kind () == EK_TU_LOCAL)
+               /* We only stream its name and location.  */
+               module->note_location (DECL_SOURCE_LOCATION (decl));
              else if (TREE_VISITED (decl))
                /* A global tree.  */;
-             else if (item->get_entity_kind () == EK_NAMESPACE)
+             else if (current->get_entity_kind () == EK_NAMESPACE)
                {
                  module->note_location (DECL_SOURCE_LOCATION (decl));
                  add_namespace_context (current, CP_DECL_CONTEXT (decl));
@@ -15224,6 +15264,12 @@ binding_cmp (const void *a_, const void *b_)
       return a_implicit ? -1 : +1;  /* Implicit first.  */
     }
 
+  /* TU-local before non-TU-local.  */
+  bool a_internal = a->get_entity_kind () == depset::EK_TU_LOCAL;
+  bool b_internal = b->get_entity_kind () == depset::EK_TU_LOCAL;
+  if (a_internal != b_internal)
+    return a_internal ? -1 : +1;  /* Internal first.  */
+
   /* Hidden before non-hidden.  */
   bool a_hidden = a->is_hidden ();
   bool b_hidden = b->is_hidden ();
@@ -15558,12 +15604,15 @@ sort_cluster (depset::hash *original, depset *scc[], 
unsigned size)
              use_lwm--;
              break;
            }
-         /* We must have copied a using, so move it too.  */
+         /* We must have copied a using or TU-local, so move it too.  */
          dep = scc[ix];
-         gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING);
+         gcc_checking_assert
+           (dep->get_entity_kind () == depset::EK_USING
+            || dep->get_entity_kind () == depset::EK_TU_LOCAL);
          /* FALLTHROUGH  */
 
        case depset::EK_USING:
+       case depset::EK_TU_LOCAL:
          if (--use_lwm != ix)
            {
              scc[ix] = scc[use_lwm];
@@ -16563,6 +16612,7 @@ enum ct_bind_flags
   cbf_export = 0x1,    /* An exported decl.  */
   cbf_hidden = 0x2,    /* A hidden (friend) decl.  */
   cbf_using = 0x4,     /* A using decl.  */
+  cbf_internal = 0x8,  /* A TU-local decl.  */
 };
 
 /* DEP belongs to a different cluster, seed it to prevent
@@ -16634,7 +16684,9 @@ module_state::write_cluster (elf_out *to, depset 
*scc[], unsigned size,
                gcc_checking_assert (dep->is_import ()
                                     || TREE_VISITED (dep->get_entity ())
                                     || (dep->get_entity_kind ()
-                                        == depset::EK_USING));
+                                        == depset::EK_USING)
+                                    || (dep->get_entity_kind ()
+                                        == depset::EK_TU_LOCAL));
              }
          }
          break;
@@ -16647,6 +16699,7 @@ module_state::write_cluster (elf_out *to, depset 
*scc[], unsigned size,
          /* FALLTHROUGH  */
 
        case depset::EK_USING:
+       case depset::EK_TU_LOCAL:
          gcc_checking_assert (!b->is_import ()
                               && !b->is_unreached ());
          dump (dumper::CLUSTER)
@@ -16719,7 +16772,9 @@ module_state::write_cluster (elf_out *to, depset 
*scc[], unsigned size,
                depset *dep = b->deps[jx];
                tree bound = dep->get_entity ();
                unsigned flags = 0;
-               if (dep->get_entity_kind () == depset::EK_USING)
+               if (dep->get_entity_kind () == depset::EK_TU_LOCAL)
+                 flags |= cbf_internal;
+               else if (dep->get_entity_kind () == depset::EK_USING)
                  {
                    tree ovl = bound;
                    bound = OVL_FUNCTION (bound);
@@ -16745,7 +16800,13 @@ module_state::write_cluster (elf_out *to, depset 
*scc[], unsigned size,
                gcc_checking_assert (DECL_P (bound));
 
                sec.i (flags);
-               sec.tree_node (bound);
+               if (flags & cbf_internal)
+                 {
+                   sec.tree_node (name_for_tu_local_decl (bound));
+                   write_location (sec, DECL_SOURCE_LOCATION (bound));
+                 }
+               else
+                 sec.tree_node (bound);
              }
 
            /* Terminate the list.  */
@@ -16754,6 +16815,7 @@ module_state::write_cluster (elf_out *to, depset 
*scc[], unsigned size,
          break;
 
        case depset::EK_USING:
+       case depset::EK_TU_LOCAL:
          dump () && dump ("Depset:%u %s %C:%N", ix, b->entity_kind_name (),
                           TREE_CODE (decl), decl);
          break;
@@ -16871,6 +16933,7 @@ module_state::read_cluster (unsigned snum)
            tree name = sec.tree_node ();
            tree decls = NULL_TREE;
            tree visible = NULL_TREE;
+           tree internal = NULL_TREE;
            tree type = NULL_TREE;
            bool dedup = false;
            bool global_p = is_header ();
@@ -16886,6 +16949,23 @@ module_state::read_cluster (unsigned snum)
                if ((flags & cbf_hidden)
                    && (flags & (cbf_using | cbf_export)))
                  sec.set_overrun ();
+               if ((flags & cbf_internal)
+                   && flags != cbf_internal)
+                 sec.set_overrun ();
+
+               if (flags & cbf_internal)
+                 {
+                   tree name = sec.tree_node ();
+                   location_t loc = read_location (sec);
+                   if (sec.get_overrun ())
+                     break;
+
+                   tree decl = make_node (TU_LOCAL_ENTITY);
+                   TU_LOCAL_ENTITY_NAME (decl) = name;
+                   TU_LOCAL_ENTITY_LOCATION (decl) = loc;
+                   internal = tree_cons (NULL_TREE, decl, internal);
+                   continue;
+                 }
 
                tree decl = sec.tree_node ();
                if (sec.get_overrun ())
@@ -16980,7 +17060,7 @@ module_state::read_cluster (unsigned snum)
                  }
              }
 
-           if (!decls)
+           if (!decls && !internal)
              sec.set_overrun ();
 
            if (sec.get_overrun ())
@@ -16989,7 +17069,7 @@ module_state::read_cluster (unsigned snum)
            dump () && dump ("Binding of %P", ns, name);
            if (!set_module_binding (ns, name, mod, global_p,
                                     is_module () || is_partition (),
-                                    decls, type, visible))
+                                    decls, type, visible, internal))
              sec.set_overrun ();
          }
          break;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 32af7a6aed9..8d7fc06f698 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -353,6 +353,8 @@ append_imported_binding_slot (tree *slot, tree name, 
unsigned ix)
 
       tree new_vec = make_binding_vec (name, want);
       BINDING_VECTOR_NUM_CLUSTERS (new_vec) = have + 1;
+      BINDING_VECTOR_INTERNAL_DECLS (new_vec)
+       = BINDING_VECTOR_INTERNAL_DECLS (*slot);
       BINDING_VECTOR_GLOBAL_DUPS_P (new_vec)
        = BINDING_VECTOR_GLOBAL_DUPS_P (*slot);
       BINDING_VECTOR_PARTITION_DUPS_P (new_vec)
@@ -1304,10 +1306,34 @@ name_lookup::adl_namespace_fns (tree scope, bitmap 
imports,
                      }
 
                    /* For lookups on the instantiation path we can see any
-                      module-linkage declaration; otherwise we should only
-                      see exported decls.  */
+                      declarations visible at any point on the path;
+                      otherwise we should only see exported decls.  */
                    if (on_inst_path)
-                     bind = STAT_DECL (bind);
+                     {
+                       /* If there are any internal functions visible, naming
+                          them outside that module is ill-formed.  */
+                       auto_diagnostic_group d;
+                       if (MODULE_BINDING_INTERNAL_DECLS_P (bind)
+                           && pedwarn (input_location, OPT_Wexternal_tu_local,
+                                       "overload set for argument-dependent "
+                                       "lookup of %<%D::%D%> in module %qs "
+                                       "contains TU-local entities",
+                                       scope, name, module_name (mod, false)))
+                         {
+                           tree *tu_locals
+                             = BINDING_VECTOR_INTERNAL_DECLS (val)->get (mod);
+                           gcc_checking_assert (tu_locals && *tu_locals);
+                           for (tree t = *tu_locals; t; t = TREE_CHAIN (t))
+                             {
+                               tree decl = TREE_VALUE (t);
+                               inform (TU_LOCAL_ENTITY_LOCATION (decl),
+                                       "ignoring %qD declared here "
+                                       "with internal linkage",
+                                       TU_LOCAL_ENTITY_NAME (decl));
+                             }
+                         }
+                       bind = STAT_DECL (bind);
+                     }
                    else
                      bind = STAT_VISIBLE (bind);
                  }
@@ -4427,18 +4453,21 @@ import_module_binding  (tree ns, tree name, unsigned 
mod, unsigned snum)
 /* An import of MODULE is binding NS::NAME.  There should be no
    existing binding for >= MODULE.  GLOBAL_P indicates whether the
    bindings include global module entities.  PARTITION_P is true if
-   it is part of the current module. VALUE and TYPE are the value
-   and type bindings. VISIBLE are the value bindings being exported.  */
+   it is part of the current module.  VALUE and TYPE are the value
+   and type bindings.  VISIBLE are the value bindings being exported.
+   INTERNAL is a TREE_LIST of any TU-local names visible for ADL.  */
 
 bool
 set_module_binding (tree ns, tree name, unsigned mod, bool global_p,
-                   bool partition_p, tree value, tree type, tree visible)
+                   bool partition_p, tree value, tree type, tree visible,
+                   tree internal)
 {
-  if (!value)
+  if (!value && !internal)
     /* Bogus BMIs could give rise to nothing to bind.  */
     return false;
 
-  gcc_assert (TREE_CODE (value) != NAMESPACE_DECL
+  gcc_assert (!value
+             || TREE_CODE (value) != NAMESPACE_DECL
              || DECL_NAMESPACE_ALIAS (value));
   gcc_checking_assert (mod);
 
@@ -4450,7 +4479,7 @@ set_module_binding (tree ns, tree name, unsigned mod, 
bool global_p,
     return false;
 
   tree bind = value;
-  if (type || visible != bind || partition_p || global_p)
+  if (type || visible != bind || internal || partition_p || global_p)
     {
       bind = stat_hack (bind, type);
       STAT_VISIBLE (bind) = visible;
@@ -4459,6 +4488,17 @@ set_module_binding (tree ns, tree name, unsigned mod, 
bool global_p,
        STAT_TYPE_VISIBLE_P (bind) = true;
     }
 
+  /* If this has internal declarations, track them for diagnostics.  */
+  if (internal)
+    {
+      if (!BINDING_VECTOR_INTERNAL_DECLS (*slot))
+       BINDING_VECTOR_INTERNAL_DECLS (*slot)
+         = module_tree_map_t::create_ggc ();
+      bool existed = BINDING_VECTOR_INTERNAL_DECLS (*slot)->put (mod, 
internal);
+      gcc_checking_assert (!existed);
+      MODULE_BINDING_INTERNAL_DECLS_P (bind) = true;
+    }
+
   /* Note if this is this-module and/or global binding.  */
   if (partition_p)
     MODULE_BINDING_PARTITION_P (bind) = true;
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 5b142e74899..3815b8c1c96 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -142,15 +142,23 @@ struct GTY(()) binding_cluster
 #define BINDING_VECTOR_CLUSTER(NODE,IX) \
   (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->vec[IX])
 
+struct module_tree_map_traits
+  : simple_hashmap_traits<int_hash<unsigned, 0>, tree> {};
+typedef hash_map<unsigned, tree, module_tree_map_traits> module_tree_map_t;
+
 struct GTY(()) tree_binding_vec {
   struct tree_base base;
   tree name;
+  module_tree_map_t *internal_decls;
   binding_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1];
 };
 
 /* The name of a module vector.  */
 #define BINDING_VECTOR_NAME(NODE) \
   (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->name)
+/* A collection of internal functions for ADL in this binding.  */
+#define BINDING_VECTOR_INTERNAL_DECLS(NODE) \
+  (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->internal_decls)
 
 /* tree_binding_vec does uses  base.u.dependence_info.base field for
    length.  It does not have lang_flag etc available!  */
@@ -166,6 +174,11 @@ struct GTY(()) tree_binding_vec {
 #define BINDING_VECTOR_PARTITION_DUPS_P(NODE) \
   (BINDING_VECTOR_CHECK (NODE)->base.volatile_flag)
 
+/* True if the binding slot has internal-linkage functions that should
+   cause ADL to error.  */
+#define MODULE_BINDING_INTERNAL_DECLS_P(NODE) \
+  (OVERLOAD_CHECK (NODE)->base.private_flag)
+
 /* These two flags indicate the provenence of the bindings on this
    particular vector slot.  We can of course determine this from slot
    number, but that's a relatively expensive lookup.  This avoids
@@ -491,7 +504,8 @@ extern bool import_module_binding (tree ctx, tree name, 
unsigned mod,
                                   unsigned snum);
 extern bool set_module_binding (tree ctx, tree name, unsigned mod,
                                bool global_p, bool partition_p,
-                               tree value, tree type, tree visible);
+                               tree value, tree type, tree visible,
+                               tree internal);
 extern void add_module_namespace_decl (tree ns, tree decl);
 
 enum WMB_Flags
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d0c13d4a24e..2a58a387abd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -272,7 +272,7 @@ in the following sections.
 -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
 -Wsized-deallocation  -Wsuggest-final-methods
 -Wsuggest-final-types  -Wsuggest-override  -Wno-template-body
--Wno-template-id-cdtor  -Wtemplate-names-tu-local
+-Wno-template-id-cdtor  -Wtemplate-names-tu-local -Wno-external-tu-local
 -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
 -Wno-virtual-move-assign  -Wvolatile}
 
@@ -4756,6 +4756,32 @@ The presence of an explicit instantiation silences the 
warning.
 
 This flag is enabled by @option{-Wextra}.
 
+@opindex Wexternal-tu-local
+@opindex Wno-external-tu-local
+@item -Wno-external-tu-local
+Warn when naming a TU-local entity outside of the translation unit it
+was declared in.  Such declarations will be ignored during name lookup.
+This can occur when performing ADL from a template declared in the same
+TU as the internal function:
+
+@smallexample
+export module M;
+template <typename T> void foo(T t) @{
+  bar(t);
+@}
+struct S @{@} s;
+static void bar(S) @{@}  // internal linkage
+
+// instantiating foo(s) from outside this TU can see ::bar,
+// but naming it there is ill-formed.
+@end smallexample
+
+This can be worked around by making @code{bar} attached to the global
+module, using @code{extern "C++"}.
+
+This warning is enabled by default, and is upgraded to an error by
+@option{-pedantic-errors}.
+
 @opindex Wterminate
 @opindex Wno-terminate
 @item -Wno-terminate @r{(C++ and Objective-C++ only)}
diff --git a/gcc/testsuite/g++.dg/modules/adl-6_c.C 
b/gcc/testsuite/g++.dg/modules/adl-6_c.C
index 99b6c4c043e..2c675f5241a 100644
--- a/gcc/testsuite/g++.dg/modules/adl-6_c.C
+++ b/gcc/testsuite/g++.dg/modules/adl-6_c.C
@@ -1,5 +1,5 @@
 // PR c++/117658
-// { dg-additional-options "-fmodules" }
+// { dg-additional-options "-fmodules -Wno-error=external-tu-local" }
 
 import N;
 
@@ -22,7 +22,8 @@ void test() {
 
   apply_err(x);  // error: R::g has internal linkage and cannot be used 
outside N
   // { dg-message "here" "" { target *-*-* } .-1 }
-  // { dg-error "'g'" "" { target *-*-* } 0 }
+  // { dg-warning "lookup of 'R::g'" "" { target *-*-* } 0 }
+  // { dg-error "'g' was not declared" "" { target *-*-* } 0 }
 
   auto y = make_Y();
   f(y);  // OK, I::B::f and I::A::Y have matching innermost non-inline 
namespace
diff --git a/gcc/testsuite/g++.dg/modules/internal-14_c.C 
b/gcc/testsuite/g++.dg/modules/internal-14_c.C
index 4f8e785ce87..50fb8e6f1a7 100644
--- a/gcc/testsuite/g++.dg/modules/internal-14_c.C
+++ b/gcc/testsuite/g++.dg/modules/internal-14_c.C
@@ -4,6 +4,6 @@
 import m;
 
 int main() {
-  // { dg-error "instantiation exposes TU-local entity '(fun1|Dodgy)'" "" { 
target *-*-* } 0 }
+  // { dg-error "instantiation exposes TU-local entity 
'\[^']*(fun1|Dodgy)\[^']*'" "" { target *-*-* } 0 }
   fun2(123);  // { dg-message "required from here" }
 }
diff --git a/gcc/testsuite/g++.dg/modules/internal-15_a.C 
b/gcc/testsuite/g++.dg/modules/internal-15_a.C
new file mode 100644
index 00000000000..03fec2a4541
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-15_a.C
@@ -0,0 +1,28 @@
+// { dg-additional-options "-fmodules -fdump-lang-module-graph 
-Wno-global-module" }
+// { dg-module-cmi A }
+
+export module A;
+
+namespace N {
+  struct A {};
+  void adl(A);
+  inline namespace inner {
+    static void adl(int);
+  }
+}
+namespace G {
+  struct B {};
+  void adl(B);
+  namespace {
+    extern "C++" void adl(int);
+  }
+}
+void adl(double);
+
+template <typename T>
+inline void h(T t) {
+  adl(t);
+}
+
+// { dg-final { scan-lang-dump {Binding on tu-local 
function_decl:'::N::inner::adl' found} module } }
+// { dg-final { scan-lang-dump-not {'G::_GLOBAL__N_1::adl'} module } }
diff --git a/gcc/testsuite/g++.dg/modules/internal-15_b.C 
b/gcc/testsuite/g++.dg/modules/internal-15_b.C
new file mode 100644
index 00000000000..003d948b014
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-15_b.C
@@ -0,0 +1,13 @@
+// { dg-additional-options "-fmodules -pedantic-errors" }
+
+module A;
+
+void other() {
+  adl(N::A{});  // OK, lookup occurs from here
+  h(0);  // OK, doesn't consider N::inner::adl
+
+  h(N::A{});  // { dg-message "required from here" }
+  // { dg-error "TU-local" "" { target *-*-* } 0 }
+
+  h(G::B{});  // OK, G::adl is not attached to A
+}
-- 
2.51.0

Reply via email to