On Tue, Sep 24, 2024 at 09:45:37AM +1000, Nathaniel Shead wrote:
> I feel like there should be a way to make use of LAMBDA_TYPE_EXTRA_SCOPE to
> avoid the need for the new TYPE_DEFINED_IN_INITIALIZER_P flag, perhaps once
> something like my patch here[1] is accepted (but with further embellishments
> for concepts, probably), but I wasn't able to work it out. Since currently as
> far as I'm aware only lambdas can satisfy being a type with no name defined in
> an 'initializer' this does seem a little overkill but I've applied it to all
> class types just in case.
> 
> [1]: https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662393.html
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> 
> -- >8 --
> 
> 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.
> 
> 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:
> 
>       * cp-tree.h (TYPE_DEPENDENT_P_VALID): Fix whitespace.
>       (TYPE_DEFINED_IN_INITIALIZER_P): New accessor.
>       * 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.
>       * parser.h (struct cp_parser::in_initializer_p): New flag.
>       * parser.cc (cp_debug_parser): Print the new flag.
>       (cp_parser_new): Set the new flag to false.
>       (cp_parser_lambda_expression): Mark whether the lambda was
>       defined in an initializer.
>       (cp_parser_initializer): Set the new flag to true while parsing.
>       (cp_parser_class_head): Mark whether the class was defined in an
>       initializer.
>       (cp_parser_concept_definition): Set the new flag to true while
>       parsing.
> 
> 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.C: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> ---
>  gcc/cp/cp-tree.h                            |   7 +-
>  gcc/cp/module.cc                            | 388 +++++++++++++++++---
>  gcc/cp/parser.cc                            |  16 +
>  gcc/cp/parser.h                             |   3 +
>  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.C   | 112 ++++++
>  gcc/testsuite/g++.dg/modules/linkage-2.C    |   5 +-
>  9 files changed, 493 insertions(+), 73 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-3.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-4.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 7baa2ccbe1e..99fbd905896 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -534,6 +534,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
>        AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM)
>        TEMPLATE_TEMPLATE_PARM_SIMPLE_P (in TEMPLATE_TEMPLATE_PARM)
>     6: TYPE_DEPENDENT_P_VALID
> +   7: TYPE_DEFINED_IN_INITIALIZER_P
>  
>     Usage of DECL_LANG_FLAG_?:
>     0: DECL_TEMPLATE_PARM_P (in PARM_DECL, CONST_DECL, TYPE_DECL, or 
> TEMPLATE_DECL)
> @@ -2292,7 +2293,11 @@ enum languages { lang_c, lang_cplusplus };
>  
>  /* True if dependent_type_p has been called for this type, with the
>     result that TYPE_DEPENDENT_P is valid.  */
> -#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6(NODE)
> +#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6 (NODE)
> +
> +/* True if this type was defined in an initializer.  Used for determining
> +   whether an entity is TU-local.  */
> +#define TYPE_DEFINED_IN_INITIALIZER_P(NODE) TYPE_LANG_FLAG_7 (NODE)
>  
>  /* Nonzero if this type is const-qualified.  */
>  #define CP_TYPE_CONST_P(NODE)                                \
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 7589de2348d..ff6891ff8d6 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -2330,10 +2330,8 @@ private:
>      DB_KIND_BITS = EK_BITS,
>      DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
>      DB_IS_MEMBER_BIT,                /* Is an out-of-class member.  */
> -    DB_IS_INTERNAL_BIT,              /* It is an (erroneous)
> -                                internal-linkage entity.  */
> -    DB_REFS_INTERNAL_BIT,    /* Refers to an internal-linkage
> -                                entity. */
> +    DB_TU_LOCAL_BIT,         /* It is a TU-local entity.  */
> +    DB_EXPOSURE_BIT,         /* Exposes a TU-local entity.  */
>      DB_IMPORTED_BIT,         /* An imported entity.  */
>      DB_UNREACHED_BIT,                /* A yet-to-be reached entity.  */
>      DB_HIDDEN_BIT,           /* A hidden binding.  */
> @@ -2414,13 +2412,13 @@ public:
>      return get_flag_bit<DB_IS_MEMBER_BIT> ();
>    }
>  public:
> -  bool is_internal () const
> +  bool is_tu_local () const
>    {
> -    return get_flag_bit<DB_IS_INTERNAL_BIT> ();
> +    return get_flag_bit<DB_TU_LOCAL_BIT> ();
>    }
> -  bool refs_internal () const
> +  bool is_exposure () const
>    {
> -    return get_flag_bit<DB_REFS_INTERNAL_BIT> ();
> +    return get_flag_bit<DB_EXPOSURE_BIT> ();
>    }
>    bool is_import () const
>    {
> @@ -2580,6 +2578,11 @@ public:
>      depset *add_dependency (tree decl, entity_kind);
>      void add_namespace_context (depset *, tree ns);
>  
> +  private:
> +    bool has_tu_local_tmpl_arg (tree decl, tree args, bool explain);
> +    bool is_tu_local_entity (tree decl, bool explain = false);
> +    bool is_tu_local_value (tree decl, tree expr, bool explain = false);
> +
>    private:
>      static bool add_binding_entity (tree, WMB_Flags, void *);
>  
> @@ -9528,6 +9531,18 @@ trees_in::tree_node (bool is_use)
>        /* NULL_TREE.  */
>        break;
>  
> +    case tt_tu_local:
> +      {
> +     /* A translation-unit local entity.  */
> +     res = make_node (TU_LOCAL_ENTITY);
> +     int tag = insert (res);
> +
> +     TU_LOCAL_ENTITY_NAME (res) = tree_node ();
> +     TU_LOCAL_ENTITY_LOCATION (res) = state->read_location (*this);
> +     dump (dumper::TREE) && dump ("Read TU-local entity:%d %N", tag, res);
> +      }
> +      break;
> +

I just noticed I messed up rebasing somehow, this hunk should be part of
the following patch.

>      case tt_fixed:
>        /* A fixed ref, find it in the fixed_ref array.   */
>        {
> @@ -12890,6 +12905,243 @@ depset::hash::find_binding (tree ctx, tree name)
>    return slot ? *slot : NULL;
>  }
>  
> +/* Returns true if DECL is a TU-local entity, as defined by [basic.link].
> +   If EXPLAIN is true, emit an informative note about why DECL is TU-local.  
> */
> +
> +bool
> +depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/)
> +{
> +  gcc_checking_assert (DECL_P (decl));
> +
> +  /* An explicit type alias is not an entity, and so is never TU-local.  */
> +  if (TREE_CODE (decl) == TYPE_DECL
> +      && !DECL_IMPLICIT_TYPEDEF_P (decl)
> +      && !DECL_SELF_REFERENCE_P (decl))
> +    return false;
> +
> +  location_t loc = DECL_SOURCE_LOCATION (decl);
> +  tree type = TREE_TYPE (decl);
> +
> +  /* Check specializations first for slightly better explanations.  */
> +  int use_tpl = -1;
> +  tree ti = node_template_info (decl, use_tpl);
> +  if (use_tpl > 0 && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL)
> +    {
> +      /* A specialization of a TU-local template.  */
> +      tree tmpl = TI_TEMPLATE (ti);
> +      if (is_tu_local_entity (tmpl))
> +     {
> +       if (explain)
> +         {
> +           inform (loc, "%qD is a specialization of TU-local template %qD",
> +                   decl, tmpl);
> +           is_tu_local_entity (tmpl, /*explain=*/true);
> +         }
> +       return true;
> +     }
> +
> +      /* A specialization of a template with any TU-local template argument. 
>  */
> +      if (has_tu_local_tmpl_arg (decl, TI_ARGS (ti), explain))
> +     return true;
> +
> +      /* FIXME A specialization of a template whose (possibly instantiated)
> +      declaration is an exposure.  This should always be covered by the
> +      above cases??  */
> +    }
> +
> +  /* A type, function, variable, or template with internal linkage.  */
> +  linkage_kind kind = decl_linkage (decl);
> +  if (kind == lk_internal)
> +    {
> +      if (explain)
> +     inform (loc, "%qD declared with internal linkage", decl);
> +      return true;
> +    }
> +
> +  /* Does not have a name with linkage and is declared, or introduced by a
> +     lambda-expression, within the definition of a TU-local entity.  */
> +  if (kind == lk_none)
> +    {
> +      tree ctx = CP_DECL_CONTEXT (decl);
> +      if (LAMBDA_TYPE_P (type))
> +     if (tree extra = LAMBDA_TYPE_EXTRA_SCOPE (type))
> +       ctx = extra;
> +
> +      if (TREE_CODE (ctx) == NAMESPACE_DECL)
> +     {
> +       if (!TREE_PUBLIC (ctx))
> +         {
> +           if (explain)
> +             inform (loc, "%qD has no linkage and is declared in an "
> +                     "anonymous namespace", decl);
> +           return true;
> +         }
> +     }
> +      else if (TYPE_P (ctx))
> +     {
> +       tree ctx_decl = TYPE_MAIN_DECL (ctx);
> +       if (is_tu_local_entity (ctx_decl))
> +         {
> +           if (explain)
> +             {
> +               inform (loc, "%qD has no linkage and is declared within "
> +                       "TU-local entity %qT", decl, ctx);
> +               is_tu_local_entity (ctx_decl, /*explain=*/true);
> +             }
> +           return true;
> +         }
> +     }
> +      else if (is_tu_local_entity (ctx))
> +     {
> +       if (explain)
> +         {
> +           inform (loc, "%qD has no linkage and is declared within "
> +                   "TU-local entity %qD", decl, ctx);
> +           is_tu_local_entity (ctx, /*explain=*/true);
> +         }
> +       return true;
> +     }
> +    }
> +
> +  /* A type with no name that is defined outside a class-specifier, function
> +     body, or initializer; or is introduced by a defining-type-specifier that
> +     is used to declare only TU-local entities.
> +
> +     We consider types with names for linkage purposes as having names, since
> +     these aren't really TU-local, and also consider constraint-expressions
> +     as initializers.  */
> +  if (TREE_CODE (decl) == TYPE_DECL
> +      && TYPE_ANON_P (type)
> +      && !DECL_SELF_REFERENCE_P (decl)
> +      /* An enum with an enumerator name for linkage.  */
> +      && !(UNSCOPED_ENUM_P (type) && TYPE_VALUES (type)))
> +    {
> +      tree main_decl = TYPE_MAIN_DECL (type);
> +      if (!TYPE_DEFINED_IN_INITIALIZER_P (type)
> +       && !DECL_CLASS_SCOPE_P (main_decl)
> +       && !decl_function_context (main_decl))
> +     {
> +       if (explain)
> +         inform (loc, "%qT has no name and is not defined within a class, "
> +                 "function, or initializer", type);
> +       return true;
> +     }
> +
> +      // FIXME introduced by a defining-type-specifier only declaring 
> TU-local
> +      // entities; does this refer to e.g. 'static struct {} a;"?  I can't
> +      // think of any cases where this isn't covered by earlier cases.  */
> +    }
> +
> +  return false;
> +}
> +
> +/* Helper for is_tu_local_entity.  Returns true if one of the ARGS of
> +   DECL is TU-local.  Emits an explanation if EXPLAIN is true.  */
> +
> +bool
> +depset::hash::has_tu_local_tmpl_arg (tree decl, tree args, bool explain)
> +{
> +  if (!args || TREE_CODE (args) != TREE_VEC)
> +    return false;
> +
> +  for (tree a : tree_vec_range (args))
> +    {
> +      if (TREE_CODE (a) == TREE_VEC)
> +     {
> +       if (has_tu_local_tmpl_arg (decl, a, explain))
> +         return true;
> +     }
> +      else if (!WILDCARD_TYPE_P (a))
> +     {
> +       if (DECL_P (a) && is_tu_local_entity (a))
> +         {
> +           if (explain)
> +             {
> +               inform (DECL_SOURCE_LOCATION (decl),
> +                       "%qD has TU-local template argument %qD",
> +                       decl, a);
> +               is_tu_local_entity (a, /*explain=*/true);
> +             }
> +           return true;
> +         }
> +
> +       if (TYPE_P (a) && TYPE_NAME (a) && is_tu_local_entity (TYPE_NAME (a)))
> +         {
> +           if (explain)
> +             {
> +               inform (DECL_SOURCE_LOCATION (decl),
> +                       "%qD has TU-local template argument %qT",
> +                       decl, a);
> +               is_tu_local_entity (TYPE_NAME (a), /*explain=*/true);
> +             }
> +           return true;
> +         }
> +
> +       if (EXPR_P (a) && is_tu_local_value (decl, a, explain))
> +         return true;
> +     }
> +    }
> +
> +  return false;
> +}
> +
> +/* Returns true if EXPR (part of the initializer for DECL) is a TU-local 
> value
> +   or object.  Emits an explanation if EXPLAIN is true.  */
> +
> +bool
> +depset::hash::is_tu_local_value (tree decl, tree expr, bool explain)
> +{
> +  if (!expr)
> +    return false;
> +
> +  tree e = expr;
> +  STRIP_ANY_LOCATION_WRAPPER (e);
> +  STRIP_NOPS (e);
> +  if (TREE_CODE (e) == TARGET_EXPR)
> +    e = TARGET_EXPR_INITIAL (e);
> +  if (!e)
> +    return false;
> +
> +  /* It is, or is a pointer to, a TU-local function or the object associated
> +     with a TU-local variable.  */
> +  tree object = NULL_TREE;
> +  if (TREE_CODE (e) == ADDR_EXPR)
> +    object = TREE_OPERAND (e, 0);
> +  else if (TREE_CODE (e) == PTRMEM_CST)
> +    object = PTRMEM_CST_MEMBER (e);
> +  else if (VAR_OR_FUNCTION_DECL_P (e))
> +    object = e;
> +
> +  if (object
> +      && VAR_OR_FUNCTION_DECL_P (object)
> +      && is_tu_local_entity (object))
> +    {
> +      if (explain)
> +     {
> +       /* We've lost a lot of location information by the time we get here,
> +          so let's just do our best effort.  */
> +       auto loc = cp_expr_loc_or_loc (expr, DECL_SOURCE_LOCATION (decl));
> +       if (VAR_P (object))
> +         inform (loc, "%qD refers to TU-local object %qD", decl, object);
> +       else
> +         inform (loc, "%qD refers to TU-local function %qD", decl, object);
> +       is_tu_local_entity (object, true);
> +     }
> +      return true;
> +    }
> +
> +  /* It is an object of class or array type and any of its subobjects or
> +     any of the objects or functions to which its non-static data members
> +     of reference type refer is TU-local and is usable in constant
> +     expressions.  */
> +  if (TREE_CODE (e) == CONSTRUCTOR && AGGREGATE_TYPE_P (TREE_TYPE (e)))
> +    for (auto& f : CONSTRUCTOR_ELTS (e))
> +      if (is_tu_local_value (decl, f.value, explain))
> +     return true;
> +
> +  return false;
> +}
> +
>  /* DECL is a newly discovered dependency.  Create the depset, if it
>     doesn't already exist.  Add it to the worklist if so.
>  
> @@ -13014,26 +13266,30 @@ depset::hash::make_dependency (tree decl, 
> entity_kind ek)
>               }
>           }
>  
> -       if (ek == EK_DECL
> +       if (!header_module_p ()
>             && !dep->is_import ()
> -           && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL
> -           && !(TREE_CODE (decl) == TEMPLATE_DECL
> -                && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)))
> -         {
> -           tree ctx = CP_DECL_CONTEXT (decl);
> +           && is_tu_local_entity (decl))
> +         /* An internal decl.  This is OK in a header unit.  */
> +         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))
> -             {
> -               /* 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> ();
> -             }
> +       if (!header_module_p ()
> +           && !dep->is_import ()
> +           && VAR_P (decl)
> +           && (TREE_CONSTANT (decl)
> +               || (TYPE_REF_P (TREE_TYPE (decl))
> +                   && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)))
> +           && is_tu_local_value (decl, DECL_INITIAL (decl)))
> +         {
> +           /* 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.  This is OK in a header unit.  */
> +             dep->set_flag_bit<DB_EXPOSURE_BIT> ();
>           }
>       }
>  
> @@ -13059,8 +13315,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 ())
> @@ -13168,13 +13424,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)
> @@ -13873,10 +14125,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.  */
> @@ -13889,23 +14139,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);
>       }
>      }
>  
> @@ -13929,7 +14197,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);
> @@ -13939,7 +14209,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;
>  
> @@ -14142,14 +14413,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);
> @@ -18411,7 +18680,8 @@ module_state::write_begin (elf_out *to, cpp_reader 
> *reader,
>    note_defs = note_defs_table_t::create_ggc (1000);
>  #endif
>  
> -  /* Determine Strongy Connected Components.  */
> +  /* Determine Strongy Connected Components.  This will also strip any
> +     unnecessary dependencies on imported or TU-local entities.  */
>    vec<depset *> sccs = table.connect ();
>  
>    vec_alloc (ool, modules->length ());
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 4dd9474cf60..f75d3f53343 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -564,6 +564,8 @@ cp_debug_parser (FILE *file, cp_parser *parser)
>                             parser->in_unbraced_export_declaration_p);
>    cp_debug_print_flag (file, "Parsing a declarator",
>                             parser->in_declarator_p);
> +  cp_debug_print_flag (file, "Parsing an initializer",
> +                           parser->in_initializer_p);
>    cp_debug_print_flag (file, "In template argument list",
>                             parser->in_template_argument_list_p);
>    cp_debug_print_flag (file, "Parsing an iteration statement",
> @@ -4455,6 +4457,9 @@ cp_parser_new (cp_lexer *lexer)
>    /* We are not processing a declarator.  */
>    parser->in_declarator_p = false;
>  
> +  /* We are not processing an initializer.  */
> +  parser->in_initializer_p = false;
> +
>    /* We are not processing a template-argument-list.  */
>    parser->in_template_argument_list_p = false;
>  
> @@ -11426,6 +11431,7 @@ cp_parser_lambda_expression (cp_parser* parser)
>  
>    record_lambda_scope (lambda_expr);
>    record_lambda_scope_discriminator (lambda_expr);
> +  TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
>  
>    /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set.  */
>    determine_visibility (TYPE_NAME (type));
> @@ -26331,6 +26337,9 @@ cp_parser_initializer (cp_parser *parser, bool 
> *is_direct_init /*=nullptr*/,
>    if (non_constant_p)
>      *non_constant_p = false;
>  
> +  bool saved_in_initializer_p = parser->in_initializer_p;
> +  parser->in_initializer_p = true;
> +
>    if (token->type == CPP_EQ)
>      {
>        /* Consume the `='.  */
> @@ -26367,6 +26376,8 @@ cp_parser_initializer (cp_parser *parser, bool 
> *is_direct_init /*=nullptr*/,
>    if (!subexpression_p && check_for_bare_parameter_packs (init))
>      init = error_mark_node;
>  
> +  parser->in_initializer_p = saved_in_initializer_p;
> +
>    return init;
>  }
>  
> @@ -27937,6 +27948,8 @@ cp_parser_class_head (cp_parser* parser,
>      }
>    else if (type == error_mark_node)
>      type = NULL_TREE;
> +  else
> +    TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
>  
>    if (type)
>      {
> @@ -31367,6 +31380,8 @@ cp_parser_concept_definition (cp_parser *parser)
>      }
>  
>    processing_constraint_expression_sentinel parsing_constraint;
> +  parser->in_initializer_p = true;
> +
>    tree init = cp_parser_constraint_expression (parser);
>    if (init == error_mark_node)
>      cp_parser_skip_to_end_of_statement (parser);
> @@ -31375,6 +31390,7 @@ cp_parser_concept_definition (cp_parser *parser)
>       but continue as if it were.  */
>    cp_parser_consume_semicolon_at_end_of_statement (parser);
>  
> +  parser->in_initializer_p = false;
>    return finish_concept_definition (id, init, attrs);
>  }
>  
> diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
> index 09b356e5e73..ee8c4f9001e 100644
> --- a/gcc/cp/parser.h
> +++ b/gcc/cp/parser.h
> @@ -323,6 +323,9 @@ struct GTY(()) cp_parser {
>       direct-declarator.  */
>    bool in_declarator_p;
>  
> +  /* TRUE if we are parsing an initializer.  */
> +  bool in_initializer_p;
> +
>    /* TRUE if we are presently parsing a template-argument-list.  */
>    bool in_template_argument_list_p;
>  
> diff --git a/gcc/testsuite/g++.dg/modules/block-decl-2.C 
> b/gcc/testsuite/g++.dg/modules/block-decl-2.C
> index d491a18dfb1..104a98ab052 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 9f7299a5fc7..3ed74c9cc42 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 00000000000..91aae32783f
> --- /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.C 
> b/gcc/testsuite/g++.dg/modules/internal-4.C
> new file mode 100644
> index 00000000000..0c9a790d379
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/internal-4.C
> @@ -0,0 +1,112 @@
> +// { dg-additional-options "-fmodules-ts" }
> +// { dg-module-cmi !bad }
> +// Test for determining various kinds of entities being marked TU-local
> +
> +export module bad;
> +
> +
> +// A type, function variable, or template with internal linkage
> +namespace {
> +  struct internal_t { int m; };
> +  enum internal_e {};
> +  void internal_f() {}
> +  int internal_v;
> +  template <typename T> void internal_x() {}
> +}
> +
> +inline void expose_type() {  // { dg-error "exposes TU-local entity" }
> +  internal_t x;
> +}
> +inline void expose_func() {  // { dg-error "exposes TU-local entity" }
> +  internal_f();
> +}
> +inline void expose_var() {  // { dg-error "exposes TU-local entity" }
> +  int* p = &internal_v;
> +}
> +inline void expose_tmpl() {  // { dg-error "exposes TU-local entity" }
> +  internal_x<int>();
> +}
> +
> +
> +// Does not have a name with linkage and is declared, or introduced by
> +// a lambda-expression, within the definition of a TU-local entity
> +static auto get_local_ok() {
> +  return 0;
> +}
> +static auto get_local_type() {
> +  struct no_linkage {};
> +  return no_linkage();
> +}
> +static auto get_local_lambda() {
> +  return []{};
> +}
> +using T = decltype(get_local_ok());  // OK
> +using U = decltype(get_local_type());  // { dg-error "exposes TU-local 
> entity" }
> +using V = decltype(get_local_lambda());  // { dg-error "exposes TU-local 
> entity" }
> +
> +static auto internal_lambda = []{ internal_f(); };  // OK
> +auto expose_lambda = internal_lambda;  // { dg-error "exposes TU-local 
> entity" }
> +
> +int not_in_tu_local
> +  = ([]{ internal_f(); }(),  // { dg-error "exposes TU-local entity" }
> +     0);
> +
> +
> +// A type with no name that is defined outside a class-specifier, function
> +// body, or initializer
> +
> +struct {} no_name;  // { dg-error "exposes TU-local entity" }
> +enum {} e;  // { dg-error "exposes TU-local entity" }
> +using not_an_initializer = class {};  // { dg-error "exposes TU-local 
> entity" }
> +
> +class in_class_specifier { struct {} x; };  // OK
> +void in_function_body() { struct {} x; }  // OK
> +auto in_initializer = []{};  // OK
> +
> +#if __cplusplus >= 202002L
> +decltype([]{}) d_lambda;  // { dg-error "exposes TU-local entity" "" { 
> target c++20 } }
> +
> +template <typename T>
> +concept in_constraint_expression = requires {
> +  // Strictly by the standard this is currently ill-formed
> +  // (this is a constraint-expression not an initializer)
> +  // but I don't think that is intended.
> +  []{};  // OK?
> +};
> +#endif
> +
> +// (But consider unnamed types with names for linkage purposes as having 
> names)
> +typedef struct {} no_name_typedef_t;
> +no_name_typedef_t linkage_name_struct;  // OK
> +
> +enum { enum_name } linkage_name_enum;  // OK
> +
> +
> +// Specialisation of a TU-local template
> +template <typename T> static void f(T) {}
> +template <> void f(int) {}  // OK
> +inline void f_use(int x) {  // { dg-error "exposes TU-local entity" }
> +  f(x);
> +}
> +
> +
> +// Specialisation of a template with any TU-local argument
> +template <typename T> void g(T) {}
> +template <> void g(internal_t) { internal_f(); }  // OK
> +template <> void g(internal_e) { internal_f(); }  // OK
> +template <> void g(decltype(no_name)) { internal_f(); }  // OK
> +template <> void g(decltype(get_local_lambda())) { internal_f(); }  // OK
> +
> +template <auto X> struct h {};
> +template struct h<&internal_v>;
> +template <> struct h<&internal_f> { internal_t x; };  // OK
> +template <> struct h<&internal_t::m> { void foo() { internal_f(); } };  // OK
> +
> +
> +// TODO: I can't come up with testcases for these that aren't already covered
> +// by one of the above cases:
> +//
> +// - A type with no name introduced by a defining-type-specifier that is
> +//   used to declare only TU-local entities
> +// - A specialisation of a template whose (possibly instantiated) declaration
> +//   is an exposure
> diff --git a/gcc/testsuite/g++.dg/modules/linkage-2.C 
> b/gcc/testsuite/g++.dg/modules/linkage-2.C
> index 4b20411572c..97421bfad8e 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" }
> -- 
> 2.46.0
> 

Reply via email to