Ping for this fix for a P1 issue.

On Sun, Feb 09, 2025 at 10:38:24PM +1100, Nathaniel Shead wrote:
> On Sun, Feb 09, 2025 at 01:16:00AM +1100, Nathaniel Shead wrote:
> > Tested on x86_64-pc-linux-gnu, OK for trunk if full bootstrap + regtest
> > passes?
> > 
> > -- >8 --
> > 
> > There are two issues with no-linkage decls (e.g. explicit type aliases)
> > in unnamed namespaces that this patch fixes.
> > 
> > Firstly, we don't currently handle exporting no-linkage decls in unnamed
> > namespaces.  This should be ill-formed in [module.export], since having
> > an exported declaration within a namespace-definition makes the
> > namespace definition exported (p2), but an unnamed namespace has
> > internal linkage thus violating p3.
> > 
> > Secondly, by the standard it appears to be possible to emit unnamed
> > namespaces from named modules in certain scenarios.  This patch makes
> > the adjustments needed to ensure we don't error in this case.
> > 
> 
> One thing to note with this is that it means that the following sample:
> 
>   export module M;
>   namespace {
>     struct Internal {};
>     using Alias = Internal;
>   }
> 
> will still error: 
> 
> test.cpp:4:9: error: ‘using {anonymous}::Alias = struct 
> {anonymous}::Internal’ exposes TU-local entity ‘struct {anonymous}::Internal’
>     4 |   using Alias = Internal;
>       |         ^~~~~
> test.cpp:3:10: note: ‘struct {anonymous}::Internal’ declared with internal 
> linkage
>     3 |   struct Internal {};
>       |          ^~~~~~~~
> 
> https://eel.is/c++draft/basic.link#17 is the relevant paragraph here,
> but I'm not 100% sure what it says about this example; I suppose that
> given Alias isn't really an entity maybe this should be OK?  But either
> way we definitely don't want to emit this alias.
> 
> Maybe the correct approach here is to mark an explicit type alias
> TU-local iff the type it refers to is itself TU-local?  So something
> like this on top of this patch:
> 
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 21251f98a35..7aa7f93df57 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -13331,6 +13331,8 @@ bool
>  depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/)
>  {
>    gcc_checking_assert (DECL_P (decl));
> +  location_t loc = DECL_SOURCE_LOCATION (decl);
> +  tree type = TREE_TYPE (decl);
> 
>    /* Only types, functions, variables, and template (specialisations)
>       can be TU-local.  */
> @@ -13340,15 +13342,22 @@ depset::hash::is_tu_local_entity (tree decl, bool 
> explain/*=false*/)
>        && TREE_CODE (decl) != TEMPLATE_DECL)
>      return false;
> 
> -  /* An explicit type alias is not an entity, and so is never TU-local.
> -     Neither are the built-in declarations of 'int' and such.  */
> +  /* An explicit type alias is not an entity, but rather inherits
> +     whether it's TU-local from the type that it aliases.
> +     The built-in declarations of 'int' and such are never TU-local.  */
>    if (TREE_CODE (decl) == TYPE_DECL
>        && !DECL_SELF_REFERENCE_P (decl)
>        && !DECL_IMPLICIT_TYPEDEF_P (decl))
> -    return false;
> -
> -  location_t loc = DECL_SOURCE_LOCATION (decl);
> -  tree type = TREE_TYPE (decl);
> +    {
> +      if (tree orig = DECL_ORIGINAL_TYPE (decl))
> +        {
> +          if (explain)
> +            inform (loc, "%qD is an alias of TU-local type %qT", decl, orig);
> +          return is_tu_local_entity (TYPE_NAME (orig), explain);
> +        }
> +      else
> +        return false;
> +    }
> 
>    /* Check specializations first for slightly better explanations.  */
>    int use_tpl = -1;
> 
> Thoughts?
> 
> >     PR c++/118799
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * module.cc (depset::hash::is_tu_local_entity): Only types,
> >     functions, variables, and template (specialisations) can be
> >     TU-local.
> >     (module_state::write_namespaces): Allow unnamed namespaces in
> >     named modules.
> >     (check_module_decl_linkage): Error for all exported declarations
> >     in an unnamed namespace.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/modules/export-6.C: Adjust error message, add check for
> >     no-linkage decls in namespace.
> >     * g++.dg/modules/internal-4_b.C: Allow exposing a namespace with
> >     internal linkage.
> >     * g++.dg/modules/using-30_a.C: New test.
> >     * g++.dg/modules/using-30_b.C: New test.
> >     * g++.dg/modules/using-30_c.C: New test.
> > 
> > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > ---
> >  gcc/cp/module.cc                            | 35 ++++++++++++++++-----
> >  gcc/testsuite/g++.dg/modules/export-6.C     | 33 ++++++++++---------
> >  gcc/testsuite/g++.dg/modules/internal-4_b.C |  4 +--
> >  gcc/testsuite/g++.dg/modules/using-30_a.C   |  9 ++++++
> >  gcc/testsuite/g++.dg/modules/using-30_b.C   |  6 ++++
> >  gcc/testsuite/g++.dg/modules/using-30_c.C   | 13 ++++++++
> >  6 files changed, 77 insertions(+), 23 deletions(-)
> >  create mode 100644 gcc/testsuite/g++.dg/modules/using-30_a.C
> >  create mode 100644 gcc/testsuite/g++.dg/modules/using-30_b.C
> >  create mode 100644 gcc/testsuite/g++.dg/modules/using-30_c.C
> > 
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index 59716e1873e..21251f98a35 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -13332,6 +13332,14 @@ depset::hash::is_tu_local_entity (tree decl, bool 
> > explain/*=false*/)
> >  {
> >    gcc_checking_assert (DECL_P (decl));
> >  
> > +  /* Only types, functions, variables, and template (specialisations)
> > +     can be TU-local.  */
> > +  if (TREE_CODE (decl) != TYPE_DECL
> > +      && TREE_CODE (decl) != FUNCTION_DECL
> > +      && TREE_CODE (decl) != VAR_DECL
> > +      && TREE_CODE (decl) != TEMPLATE_DECL)
> > +    return false;
> > +
> >    /* An explicit type alias is not an entity, and so is never TU-local.
> >       Neither are the built-in declarations of 'int' and such.  */
> >    if (TREE_CODE (decl) == TYPE_DECL
> > @@ -16433,8 +16441,9 @@ module_state::write_namespaces (elf_out *to, 
> > vec<depset *> spaces,
> >        depset *b = spaces[ix];
> >        tree ns = b->get_entity ();
> >  
> > +      /* This could be an anonymous namespace even for a named module,
> > +    since we can still emit no-linkage decls.  */
> >        gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
> > -      gcc_checking_assert (TREE_PUBLIC (ns) || header_module_p ());
> >  
> >        unsigned flags = 0;
> >        if (TREE_PUBLIC (ns))
> > @@ -20394,13 +20403,25 @@ check_module_decl_linkage (tree decl)
> >    /* An internal-linkage declaration cannot be generally be exported.
> >       But it's OK to export any declaration from a header unit, including
> >       internal linkage declarations.  */
> > -  if (!header_module_p ()
> > -      && DECL_MODULE_EXPORT_P (decl)
> > -      && decl_linkage (decl) == lk_internal)
> > +  if (!header_module_p () && DECL_MODULE_EXPORT_P (decl))
> >      {
> > -      error_at (DECL_SOURCE_LOCATION (decl),
> > -           "exporting declaration %qD with internal linkage", decl);
> > -      DECL_MODULE_EXPORT_P (decl) = false;
> > +      /* Let's additionally treat any exported declaration within an
> > +    internal namespace as exporting a declaration with internal
> > +    linkage, as this would implicitly also export the internal
> > +    linkage namespace.  */
> > +      if (decl_internal_context_p (decl))
> > +   {
> > +     error_at (DECL_SOURCE_LOCATION (decl),
> > +               "exporting declaration %qD declared in unnamed namespace",
> > +               decl);
> > +     DECL_MODULE_EXPORT_P (decl) = false;
> > +   }
> > +      else if (decl_linkage (decl) == lk_internal)
> > +   {
> > +     error_at (DECL_SOURCE_LOCATION (decl),
> > +               "exporting declaration %qD with internal linkage", decl);
> > +     DECL_MODULE_EXPORT_P (decl) = false;
> > +   }
> >      }
> >  }
> >  
> > diff --git a/gcc/testsuite/g++.dg/modules/export-6.C 
> > b/gcc/testsuite/g++.dg/modules/export-6.C
> > index 460cdf08ea9..af54e5fbe87 100644
> > --- a/gcc/testsuite/g++.dg/modules/export-6.C
> > +++ b/gcc/testsuite/g++.dg/modules/export-6.C
> > @@ -16,27 +16,32 @@ export static auto [d] = S{};  // { dg-error "internal 
> > linkage" "" { target c++2
> >  #endif
> >  
> >  namespace {
> > -  export int y = 456;  // { dg-error "internal linkage" }
> > -  export void h();  // { dg-error "internal linkage" }
> > -  export void i() {}  // { dg-error "internal linkage" }
> > -  export template <typename T> void v(); // { dg-error "internal linkage" }
> > -  export template <typename T> void w() {} // { dg-error "internal 
> > linkage" }
> > -  export auto [e] = S{};  // { dg-error "internal linkage" }
> > +  export int y = 456;  // { dg-error "exporting" }
> > +  export void h();  // { dg-error "exporting" }
> > +  export void i() {}  // { dg-error "exporting" }
> > +  export template <typename T> void v(); // { dg-error "exporting" }
> > +  export template <typename T> void w() {} // { dg-error "exporting" }
> > +  export auto [e] = S{};  // { dg-error "exporting" }
> >  
> > -  export namespace ns {}  // { dg-error "internal linkage" }
> > -  export namespace alias = global;  // { dg-error "internal linkage" }
> > +  export namespace ns {}  // { dg-error "exporting" }
> > +  export namespace alias = global;  // { dg-error "exporting" }
> >  
> > -  export struct A {};  // { dg-error "internal linkage" }
> > -  export template <typename T> struct B {};  // { dg-error "internal 
> > linkage" }
> > +  export struct A {};  // { dg-error "exporting" }
> > +  export template <typename T> struct B {};  // { dg-error "exporting" }
> >  
> > -  export enum E {};  // { dg-error "internal linkage" }
> > -  export enum class F {};  // { dg-error "internal linkage" }
> > +  export enum E {};  // { dg-error "exporting" }
> > +  export enum class F {};  // { dg-error "exporting" }
> >  
> > -  export template <typename T> using U = int;  // { dg-error "internal 
> > linkage" }
> > +  export template <typename T> using U = int;  // { dg-error "exporting" }
> >  
> >  #if __cplusplus >= 202002L
> > -  export template <typename T> concept C = true;  // { dg-error "internal 
> > linkage" "" { target c++20 } }
> > +  export template <typename T> concept C = true;  // { dg-error 
> > "exporting" "" { target c++20 } }
> >  #endif
> > +
> > +  // Also complain about exporting no-linkage decls in an unnamed namespace
> > +  export typedef int T;  // { dg-error "exporting" }
> > +  export typedef struct {} *PC;  // { dg-error "exporting" }
> > +  export using V = int;  // { dg-error "exporting" }
> >  }
> >  
> >  export namespace {}  // { dg-error "exporting unnamed namespace" }
> > diff --git a/gcc/testsuite/g++.dg/modules/internal-4_b.C 
> > b/gcc/testsuite/g++.dg/modules/internal-4_b.C
> > index 81fc65a69bd..7e4fbfeb1fb 100644
> > --- a/gcc/testsuite/g++.dg/modules/internal-4_b.C
> > +++ b/gcc/testsuite/g++.dg/modules/internal-4_b.C
> > @@ -32,8 +32,8 @@ 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" }
> > +// A namespace with internal linkage is not inherently an exposure.
> > +namespace expose_ns = internal_ns;  // { dg-bogus "exposes TU-local 
> > entity" }
> >  
> >  // But we don't consider a weakref as being TU-local, despite being
> >  // marked static; this is to support uses of weakrefs in header files
> > diff --git a/gcc/testsuite/g++.dg/modules/using-30_a.C 
> > b/gcc/testsuite/g++.dg/modules/using-30_a.C
> > new file mode 100644
> > index 00000000000..fc6a67f2bae
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/using-30_a.C
> > @@ -0,0 +1,9 @@
> > +// { dg-additional-options "-fmodules" }
> > +// { dg-module-cmi M }
> > +
> > +export module M;
> > +
> > +namespace {
> > +  using A = int;
> > +  typedef int B;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/modules/using-30_b.C 
> > b/gcc/testsuite/g++.dg/modules/using-30_b.C
> > new file mode 100644
> > index 00000000000..ba06c9de792
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/using-30_b.C
> > @@ -0,0 +1,6 @@
> > +// { dg-additional-options "-fmodules" }
> > +
> > +module M;
> > +
> > +A x;
> > +B y;
> > diff --git a/gcc/testsuite/g++.dg/modules/using-30_c.C 
> > b/gcc/testsuite/g++.dg/modules/using-30_c.C
> > new file mode 100644
> > index 00000000000..34dc6f2c98d
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/using-30_c.C
> > @@ -0,0 +1,13 @@
> > +// { dg-additional-options "-fmodules" }
> > +// The different declarations in the anonymous namespace shouldn't clash 
> > with
> > +// those in M.
> > +
> > +namespace {
> > +  using A = double;
> > +  typedef double B;
> > +}
> > +import M;
> > +int main() {
> > +  A a = 1.0;
> > +  B b = 2.0;
> > +}
> > -- 
> > 2.47.0
> > 

Reply via email to