diff mbox series

[04/10] c++/modules: Fix linkage checks for exported using-decls

Message ID 66f1fd74.a70a0220.265533.05f6@mx.google.com
State New
Headers show
Series c++/modules: Implement P1815 "Translation-unit-local entities" | expand

Commit Message

Nathaniel Shead Sept. 23, 2024, 11:44 p.m. UTC
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

-- >8 --

This fixes some inconsistencies with what kinds of linkage various
entities are assumed to have.  This also fixes handling of exported
using-decls binding to GM entities and type aliases to better align with
the standard's requirements.

gcc/cp/ChangeLog:

	* name-lookup.cc (check_can_export_using_decl): Handle internal
	linkage GM entities, and use linkage of entity ultimately
	referred to by aliases.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/using-10.C: Add tests for no-linkage, fix
	expected linkage of aliases.
	* g++.dg/modules/using-12.C: Likewise.
	* g++.dg/modules/using-27.C: New test.
	* g++.dg/modules/using-28_a.C: New test.
	* g++.dg/modules/using-28_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/name-lookup.cc                     | 55 ++++++++++++----------
 gcc/testsuite/g++.dg/modules/using-10.C   | 56 ++++++++++++++++++-----
 gcc/testsuite/g++.dg/modules/using-12.C   | 42 +++++++++++++++--
 gcc/testsuite/g++.dg/modules/using-27.C   | 14 ++++++
 gcc/testsuite/g++.dg/modules/using-28_a.C | 12 +++++
 gcc/testsuite/g++.dg/modules/using-28_b.C |  8 ++++
 6 files changed, 145 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/using-27.C
 create mode 100644 gcc/testsuite/g++.dg/modules/using-28_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/using-28_b.C

Comments

Nathaniel Shead Sept. 24, 2024, 6:53 a.m. UTC | #1
On Tue, Sep 24, 2024 at 09:44:48AM +1000, Nathaniel Shead wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> 
> -- >8 --
> 
> This fixes some inconsistencies with what kinds of linkage various
> entities are assumed to have.  This also fixes handling of exported
> using-decls binding to GM entities and type aliases to better align with
> the standard's requirements.
> 

Linaro picked up a regression on ARM from this patch where the standard
library headers are exporting using-decls of internal linkage entities,
which is supposed to be valid (and I'd missed in this version of the
patch).

Here's an updated version of the patch which handles this case and adds
a dedicated testcase for it.

Bootstrapped and regtested on x86_64-pc-linux-gnu and
aarch64-unknown-linux-gnu.

-- >8 --

This fixes some inconsistencies with what kinds of linkage various
entities are assumed to have.  This also fixes handling of exported
using-decls binding to GM entities and type aliases to better align with
the standard's requirements.

gcc/cp/ChangeLog:

	* name-lookup.cc (check_can_export_using_decl): Handle internal
	linkage GM entities (but ignore in header units); use linkage
	of entity ultimately referred to by aliases.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/using-10.C: Add tests for no-linkage, fix
	expected linkage of aliases.
	* g++.dg/modules/using-12.C: Likewise.
	* g++.dg/modules/using-27.C: New test.
	* g++.dg/modules/using-28_a.C: New test.
	* g++.dg/modules/using-28_b.C: New test.
	* g++.dg/modules/using-29.H: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/name-lookup.cc                     | 57 +++++++++++++----------
 gcc/testsuite/g++.dg/modules/using-10.C   | 56 +++++++++++++++++-----
 gcc/testsuite/g++.dg/modules/using-12.C   | 42 +++++++++++++++--
 gcc/testsuite/g++.dg/modules/using-27.C   | 14 ++++++
 gcc/testsuite/g++.dg/modules/using-28_a.C | 12 +++++
 gcc/testsuite/g++.dg/modules/using-28_b.C |  8 ++++
 gcc/testsuite/g++.dg/modules/using-29.H   |  6 +++
 7 files changed, 154 insertions(+), 41 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/using-27.C
 create mode 100644 gcc/testsuite/g++.dg/modules/using-28_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/using-28_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/using-29.H

diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index c0f89f98d87..eb365b259d9 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -5206,38 +5206,47 @@ pushdecl_outermost_localscope (tree x)
 static bool
 check_can_export_using_decl (tree binding)
 {
-  tree decl = STRIP_TEMPLATE (binding);
-
-  /* Linkage is determined by the owner of an enumerator.  */
-  if (TREE_CODE (decl) == CONST_DECL)
-    decl = TYPE_NAME (DECL_CONTEXT (decl));
+  /* Declarations in header units are always OK.  */
+  if (header_module_p ())
+    return true;
 
-  /* If the using decl is exported, the things it refers
-     to must also be exported (or not have module attachment).  */
-  if (!DECL_MODULE_EXPORT_P (decl)
-      && (DECL_LANG_SPECIFIC (decl)
-	  && DECL_MODULE_ATTACH_P (decl)))
+  /* We want the linkage of the underlying entity, so strip typedefs.
+     If the underlying entity is a builtin type then we're OK.  */
+  tree entity = binding;
+  if (TREE_CODE (entity) == TYPE_DECL)
     {
-      bool internal_p = !TREE_PUBLIC (decl);
+      entity = TYPE_MAIN_DECL (TREE_TYPE (entity));
+      if (!entity)
+	return true;
+    }
 
-      /* A template in an anonymous namespace doesn't constrain TREE_PUBLIC
-	 until it's instantiated, so double-check its context.  */
-      if (!internal_p && TREE_CODE (binding) == TEMPLATE_DECL)
-	internal_p = decl_internal_context_p (decl);
+  linkage_kind linkage = decl_linkage (entity);
+  tree not_tmpl = STRIP_TEMPLATE (entity);
 
+  /* Attachment is determined by the owner of an enumerator.  */
+  if (TREE_CODE (not_tmpl) == CONST_DECL)
+    not_tmpl = TYPE_NAME (DECL_CONTEXT (not_tmpl));
+
+  /* If the using decl is exported, the things it refers to must
+     have external linkage.  decl_linkage returns lk_external for
+     module linkage so also check for attachment.  */
+  if (linkage != lk_external
+      || (DECL_LANG_SPECIFIC (not_tmpl)
+	  && DECL_MODULE_ATTACH_P (not_tmpl)
+	  && !DECL_MODULE_EXPORT_P (not_tmpl)))
+    {
       auto_diagnostic_group d;
       error ("exporting %q#D that does not have external linkage",
 	     binding);
-      if (TREE_CODE (decl) == TYPE_DECL && !DECL_IMPLICIT_TYPEDEF_P (decl))
-	/* An un-exported explicit type alias has no linkage.  */
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with no linkage", binding);
-      else if (internal_p)
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with internal linkage", binding);
+      if (linkage == lk_none)
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with no linkage", entity);
+      else if (linkage == lk_internal)
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with internal linkage", entity);
       else
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with module linkage", binding);
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with module linkage", entity);
       return false;
     }
 
diff --git a/gcc/testsuite/g++.dg/modules/using-10.C b/gcc/testsuite/g++.dg/modules/using-10.C
index d468a36f5d8..6f82b5dd147 100644
--- a/gcc/testsuite/g++.dg/modules/using-10.C
+++ b/gcc/testsuite/g++.dg/modules/using-10.C
@@ -23,6 +23,13 @@ namespace s {
   }
 }
 
+export using s::a1;  // { dg-error "does not have external linkage" }
+export using s::b1;  // { dg-error "does not have external linkage" }
+export using s::x1;  // { dg-error "does not have external linkage" }
+export using s::y1;  // { dg-error "does not have external linkage" }
+export using s::f1;  // { dg-error "does not have external linkage" }
+export using s::g1;  // { dg-error "does not have external linkage" }
+
 // module linkage
 namespace m {
   struct a2 {};  // { dg-message "declared here with module linkage" }
@@ -41,13 +48,6 @@ namespace m {
   void g2();  // { dg-message "declared here with module linkage" }
 }
 
-export using s::a1;  // { dg-error "does not have external linkage" }
-export using s::b1;  // { dg-error "does not have external linkage" }
-export using s::x1;  // { dg-error "does not have external linkage" }
-export using s::y1;  // { dg-error "does not have external linkage" }
-export using s::f1;  // { dg-error "does not have external linkage" }
-export using s::g1;  // { dg-error "does not have external linkage" }
-
 export using m::a2;  // { dg-error "does not have external linkage" }
 export using m::b2;  // { dg-error "does not have external linkage" }
 export using m::x2;  // { dg-error "does not have external linkage" }
@@ -55,15 +55,47 @@ export using m::y2;  // { dg-error "does not have external linkage" }
 export using m::f2;  // { dg-error "does not have external linkage" }
 export using m::g2;  // { dg-error "does not have external linkage" }
 
+// no linkage
+namespace n {
+  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
+
+  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
+  using b3 = decltype(tmp_s);
+
+  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
+  using c3 = decltype(tmp_e);
+
+  auto foo() {
+    struct s {};  // { dg-message "declared here with no linkage" }
+    return s{};
+  }
+  using d3 = decltype(foo());
+}
+
+export using n::a3;  // { dg-error "does not have external linkage" }
+export using n::b3;  // { dg-error "does not have external linkage" }
+export using n::c3;  // { dg-error "does not have external linkage" }
+export using n::d3;  // { dg-error "does not have external linkage" }
+
+// typedefs
 namespace t {
-  using a = int;  // { dg-message "declared here with no linkage" }
+  // aliases have the linkage of the entity they ultimately refer to
+  using a = int;
+  typedef a b;
 
+  // a template is not an alias
   template <typename T>
-  using b = int;  // { dg-message "declared here with no linkage" }
+  using c = int;  // { dg-message "declared here with module linkage" }
+
+  // anonymous type with typedef name for linkage purposes
+  typedef struct {} d;  // { dg-message "declared here with module linkage" }
 
-  typedef int c;  // { dg-message "declared here with no linkage" }
+  // non-empty enum gets linkage of enumerator name
+  enum { X } e;  // { dg-message "declared here with module linkage"} 
 }
 
-export using t::a;  // { dg-error "does not have external linkage" }
-export using t::b;  // { dg-error "does not have external linkage" }
+export using t::a;
+export using t::b;
 export using t::c;  // { dg-error "does not have external linkage" }
+export using t::d;  // { dg-error "does not have external linkage" }
+export using t::e;  // { dg-error "does not have external linkage" }
diff --git a/gcc/testsuite/g++.dg/modules/using-12.C b/gcc/testsuite/g++.dg/modules/using-12.C
index 52ef3c6285d..4fd71696f62 100644
--- a/gcc/testsuite/g++.dg/modules/using-12.C
+++ b/gcc/testsuite/g++.dg/modules/using-12.C
@@ -57,15 +57,47 @@ namespace m {
   export using m::g2;  // { dg-error "does not have external linkage" }
 }
 
+// no linkage
+namespace n {
+  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
+
+  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
+  using b3 = decltype(tmp_s);
+
+  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
+  using c3 = decltype(tmp_e);
+
+  auto foo() {
+    struct s {};  // { dg-message "declared here with no linkage" }
+    return s{};
+  }
+  using d3 = decltype(foo());
+
+  export using n::a3;  // { dg-error "does not have external linkage" }
+  export using n::b3;  // { dg-error "does not have external linkage" }
+  export using n::c3;  // { dg-error "does not have external linkage" }
+  export using n::d3;  // { dg-error "does not have external linkage" }
+}
+
+// typedefs
 namespace t {
-  using a = int;  // { dg-message "declared here with no linkage" }
+  // aliases have the linkage of the entity they ultimately refer to
+  using a = int;
+  typedef a b;
 
+  // a template is not an alias
   template <typename T>
-  using b = int;  // { dg-message "declared here with no linkage" }
+  using c = int;  // { dg-message "declared here with module linkage" }
+
+  // anonymous type with typedef name for linkage purposes
+  typedef struct {} d;  // { dg-message "declared here with module linkage" }
 
-  typedef int c;  // { dg-message "declared here with no linkage" }
+  // non-empty enum gets linkage of enumerator name
+  enum { X } e;  // { dg-message "declared here with module linkage" }
 
-  export using t::a;  // { dg-error "does not have external linkage" }
-  export using t::b;  // { dg-error "does not have external linkage" }
+  export using t::a;
+  export using t::b;
   export using t::c;  // { dg-error "does not have external linkage" }
+  export using t::d;  // { dg-error "does not have external linkage" }
+  export using t::e;  // { dg-error "does not have external linkage" }
 }
diff --git a/gcc/testsuite/g++.dg/modules/using-27.C b/gcc/testsuite/g++.dg/modules/using-27.C
new file mode 100644
index 00000000000..857d7d4a035
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-27.C
@@ -0,0 +1,14 @@
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi !bad }
+
+module;
+
+static int x = 123;   // { dg-message "declared here with internal linkage" }
+static void f() {}    // { dg-message "declared here with internal linkage" }
+using T = struct {};  // { dg-message "declared here with no linkage" }
+
+export module bad;
+
+export using ::x;  // { dg-error "does not have external linkage" }
+export using ::f;  // { dg-error "does not have external linkage" }
+export using ::T;  // { dg-error "does not have external linkage" }
diff --git a/gcc/testsuite/g++.dg/modules/using-28_a.C b/gcc/testsuite/g++.dg/modules/using-28_a.C
new file mode 100644
index 00000000000..96bbef57f64
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-28_a.C
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi M }
+// Test that typedef names correctly provide external linkage
+
+module;
+typedef struct { int x; } A;
+export module M;
+
+export typedef struct {} B;
+
+export using ::A;
+export using ::B;
diff --git a/gcc/testsuite/g++.dg/modules/using-28_b.C b/gcc/testsuite/g++.dg/modules/using-28_b.C
new file mode 100644
index 00000000000..72876b517ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-28_b.C
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+int main() {
+  A a { 10 };
+  B b;
+}
diff --git a/gcc/testsuite/g++.dg/modules/using-29.H b/gcc/testsuite/g++.dg/modules/using-29.H
new file mode 100644
index 00000000000..ea44e0a78cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-29.H
@@ -0,0 +1,6 @@
+// { dg-additional-options "-fmodule-header" }
+
+static int foo = 123;
+namespace ns {
+  using ::foo;  // OK, we're in a header unit
+}
Jason Merrill Sept. 26, 2024, 9:31 p.m. UTC | #2
On 9/24/24 2:53 AM, Nathaniel Shead wrote:
> On Tue, Sep 24, 2024 at 09:44:48AM +1000, Nathaniel Shead wrote:
>> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
>>
>> -- >8 --
>>
>> This fixes some inconsistencies with what kinds of linkage various
>> entities are assumed to have.  This also fixes handling of exported
>> using-decls binding to GM entities and type aliases to better align with
>> the standard's requirements.
>>
> 
> Linaro picked up a regression on ARM from this patch where the standard
> library headers are exporting using-decls of internal linkage entities,
> which is supposed to be valid (and I'd missed in this version of the
> patch).
> 
> Here's an updated version of the patch which handles this case and adds
> a dedicated testcase for it.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu and
> aarch64-unknown-linux-gnu.

OK.

> -- >8 --
> 
> This fixes some inconsistencies with what kinds of linkage various
> entities are assumed to have.  This also fixes handling of exported
> using-decls binding to GM entities and type aliases to better align with
> the standard's requirements.
> 
> gcc/cp/ChangeLog:
> 
> 	* name-lookup.cc (check_can_export_using_decl): Handle internal
> 	linkage GM entities (but ignore in header units); use linkage
> 	of entity ultimately referred to by aliases.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/modules/using-10.C: Add tests for no-linkage, fix
> 	expected linkage of aliases.
> 	* g++.dg/modules/using-12.C: Likewise.
> 	* g++.dg/modules/using-27.C: New test.
> 	* g++.dg/modules/using-28_a.C: New test.
> 	* g++.dg/modules/using-28_b.C: New test.
> 	* g++.dg/modules/using-29.H: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> ---
>   gcc/cp/name-lookup.cc                     | 57 +++++++++++++----------
>   gcc/testsuite/g++.dg/modules/using-10.C   | 56 +++++++++++++++++-----
>   gcc/testsuite/g++.dg/modules/using-12.C   | 42 +++++++++++++++--
>   gcc/testsuite/g++.dg/modules/using-27.C   | 14 ++++++
>   gcc/testsuite/g++.dg/modules/using-28_a.C | 12 +++++
>   gcc/testsuite/g++.dg/modules/using-28_b.C |  8 ++++
>   gcc/testsuite/g++.dg/modules/using-29.H   |  6 +++
>   7 files changed, 154 insertions(+), 41 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/modules/using-27.C
>   create mode 100644 gcc/testsuite/g++.dg/modules/using-28_a.C
>   create mode 100644 gcc/testsuite/g++.dg/modules/using-28_b.C
>   create mode 100644 gcc/testsuite/g++.dg/modules/using-29.H
> 
> diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
> index c0f89f98d87..eb365b259d9 100644
> --- a/gcc/cp/name-lookup.cc
> +++ b/gcc/cp/name-lookup.cc
> @@ -5206,38 +5206,47 @@ pushdecl_outermost_localscope (tree x)
>   static bool
>   check_can_export_using_decl (tree binding)
>   {
> -  tree decl = STRIP_TEMPLATE (binding);
> -
> -  /* Linkage is determined by the owner of an enumerator.  */
> -  if (TREE_CODE (decl) == CONST_DECL)
> -    decl = TYPE_NAME (DECL_CONTEXT (decl));
> +  /* Declarations in header units are always OK.  */
> +  if (header_module_p ())
> +    return true;
>   
> -  /* If the using decl is exported, the things it refers
> -     to must also be exported (or not have module attachment).  */
> -  if (!DECL_MODULE_EXPORT_P (decl)
> -      && (DECL_LANG_SPECIFIC (decl)
> -	  && DECL_MODULE_ATTACH_P (decl)))
> +  /* We want the linkage of the underlying entity, so strip typedefs.
> +     If the underlying entity is a builtin type then we're OK.  */
> +  tree entity = binding;
> +  if (TREE_CODE (entity) == TYPE_DECL)
>       {
> -      bool internal_p = !TREE_PUBLIC (decl);
> +      entity = TYPE_MAIN_DECL (TREE_TYPE (entity));
> +      if (!entity)
> +	return true;
> +    }
>   
> -      /* A template in an anonymous namespace doesn't constrain TREE_PUBLIC
> -	 until it's instantiated, so double-check its context.  */
> -      if (!internal_p && TREE_CODE (binding) == TEMPLATE_DECL)
> -	internal_p = decl_internal_context_p (decl);
> +  linkage_kind linkage = decl_linkage (entity);
> +  tree not_tmpl = STRIP_TEMPLATE (entity);
>   
> +  /* Attachment is determined by the owner of an enumerator.  */
> +  if (TREE_CODE (not_tmpl) == CONST_DECL)
> +    not_tmpl = TYPE_NAME (DECL_CONTEXT (not_tmpl));
> +
> +  /* If the using decl is exported, the things it refers to must
> +     have external linkage.  decl_linkage returns lk_external for
> +     module linkage so also check for attachment.  */
> +  if (linkage != lk_external
> +      || (DECL_LANG_SPECIFIC (not_tmpl)
> +	  && DECL_MODULE_ATTACH_P (not_tmpl)
> +	  && !DECL_MODULE_EXPORT_P (not_tmpl)))
> +    {
>         auto_diagnostic_group d;
>         error ("exporting %q#D that does not have external linkage",
>   	     binding);
> -      if (TREE_CODE (decl) == TYPE_DECL && !DECL_IMPLICIT_TYPEDEF_P (decl))
> -	/* An un-exported explicit type alias has no linkage.  */
> -	inform (DECL_SOURCE_LOCATION (binding),
> -		"%q#D declared here with no linkage", binding);
> -      else if (internal_p)
> -	inform (DECL_SOURCE_LOCATION (binding),
> -		"%q#D declared here with internal linkage", binding);
> +      if (linkage == lk_none)
> +	inform (DECL_SOURCE_LOCATION (entity),
> +		"%q#D declared here with no linkage", entity);
> +      else if (linkage == lk_internal)
> +	inform (DECL_SOURCE_LOCATION (entity),
> +		"%q#D declared here with internal linkage", entity);
>         else
> -	inform (DECL_SOURCE_LOCATION (binding),
> -		"%q#D declared here with module linkage", binding);
> +	inform (DECL_SOURCE_LOCATION (entity),
> +		"%q#D declared here with module linkage", entity);
>         return false;
>       }
>   
> diff --git a/gcc/testsuite/g++.dg/modules/using-10.C b/gcc/testsuite/g++.dg/modules/using-10.C
> index d468a36f5d8..6f82b5dd147 100644
> --- a/gcc/testsuite/g++.dg/modules/using-10.C
> +++ b/gcc/testsuite/g++.dg/modules/using-10.C
> @@ -23,6 +23,13 @@ namespace s {
>     }
>   }
>   
> +export using s::a1;  // { dg-error "does not have external linkage" }
> +export using s::b1;  // { dg-error "does not have external linkage" }
> +export using s::x1;  // { dg-error "does not have external linkage" }
> +export using s::y1;  // { dg-error "does not have external linkage" }
> +export using s::f1;  // { dg-error "does not have external linkage" }
> +export using s::g1;  // { dg-error "does not have external linkage" }
> +
>   // module linkage
>   namespace m {
>     struct a2 {};  // { dg-message "declared here with module linkage" }
> @@ -41,13 +48,6 @@ namespace m {
>     void g2();  // { dg-message "declared here with module linkage" }
>   }
>   
> -export using s::a1;  // { dg-error "does not have external linkage" }
> -export using s::b1;  // { dg-error "does not have external linkage" }
> -export using s::x1;  // { dg-error "does not have external linkage" }
> -export using s::y1;  // { dg-error "does not have external linkage" }
> -export using s::f1;  // { dg-error "does not have external linkage" }
> -export using s::g1;  // { dg-error "does not have external linkage" }
> -
>   export using m::a2;  // { dg-error "does not have external linkage" }
>   export using m::b2;  // { dg-error "does not have external linkage" }
>   export using m::x2;  // { dg-error "does not have external linkage" }
> @@ -55,15 +55,47 @@ export using m::y2;  // { dg-error "does not have external linkage" }
>   export using m::f2;  // { dg-error "does not have external linkage" }
>   export using m::g2;  // { dg-error "does not have external linkage" }
>   
> +// no linkage
> +namespace n {
> +  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
> +
> +  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
> +  using b3 = decltype(tmp_s);
> +
> +  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
> +  using c3 = decltype(tmp_e);
> +
> +  auto foo() {
> +    struct s {};  // { dg-message "declared here with no linkage" }
> +    return s{};
> +  }
> +  using d3 = decltype(foo());
> +}
> +
> +export using n::a3;  // { dg-error "does not have external linkage" }
> +export using n::b3;  // { dg-error "does not have external linkage" }
> +export using n::c3;  // { dg-error "does not have external linkage" }
> +export using n::d3;  // { dg-error "does not have external linkage" }
> +
> +// typedefs
>   namespace t {
> -  using a = int;  // { dg-message "declared here with no linkage" }
> +  // aliases have the linkage of the entity they ultimately refer to
> +  using a = int;
> +  typedef a b;
>   
> +  // a template is not an alias
>     template <typename T>
> -  using b = int;  // { dg-message "declared here with no linkage" }
> +  using c = int;  // { dg-message "declared here with module linkage" }
> +
> +  // anonymous type with typedef name for linkage purposes
> +  typedef struct {} d;  // { dg-message "declared here with module linkage" }
>   
> -  typedef int c;  // { dg-message "declared here with no linkage" }
> +  // non-empty enum gets linkage of enumerator name
> +  enum { X } e;  // { dg-message "declared here with module linkage"}
>   }
>   
> -export using t::a;  // { dg-error "does not have external linkage" }
> -export using t::b;  // { dg-error "does not have external linkage" }
> +export using t::a;
> +export using t::b;
>   export using t::c;  // { dg-error "does not have external linkage" }
> +export using t::d;  // { dg-error "does not have external linkage" }
> +export using t::e;  // { dg-error "does not have external linkage" }
> diff --git a/gcc/testsuite/g++.dg/modules/using-12.C b/gcc/testsuite/g++.dg/modules/using-12.C
> index 52ef3c6285d..4fd71696f62 100644
> --- a/gcc/testsuite/g++.dg/modules/using-12.C
> +++ b/gcc/testsuite/g++.dg/modules/using-12.C
> @@ -57,15 +57,47 @@ namespace m {
>     export using m::g2;  // { dg-error "does not have external linkage" }
>   }
>   
> +// no linkage
> +namespace n {
> +  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
> +
> +  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
> +  using b3 = decltype(tmp_s);
> +
> +  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
> +  using c3 = decltype(tmp_e);
> +
> +  auto foo() {
> +    struct s {};  // { dg-message "declared here with no linkage" }
> +    return s{};
> +  }
> +  using d3 = decltype(foo());
> +
> +  export using n::a3;  // { dg-error "does not have external linkage" }
> +  export using n::b3;  // { dg-error "does not have external linkage" }
> +  export using n::c3;  // { dg-error "does not have external linkage" }
> +  export using n::d3;  // { dg-error "does not have external linkage" }
> +}
> +
> +// typedefs
>   namespace t {
> -  using a = int;  // { dg-message "declared here with no linkage" }
> +  // aliases have the linkage of the entity they ultimately refer to
> +  using a = int;
> +  typedef a b;
>   
> +  // a template is not an alias
>     template <typename T>
> -  using b = int;  // { dg-message "declared here with no linkage" }
> +  using c = int;  // { dg-message "declared here with module linkage" }
> +
> +  // anonymous type with typedef name for linkage purposes
> +  typedef struct {} d;  // { dg-message "declared here with module linkage" }
>   
> -  typedef int c;  // { dg-message "declared here with no linkage" }
> +  // non-empty enum gets linkage of enumerator name
> +  enum { X } e;  // { dg-message "declared here with module linkage" }
>   
> -  export using t::a;  // { dg-error "does not have external linkage" }
> -  export using t::b;  // { dg-error "does not have external linkage" }
> +  export using t::a;
> +  export using t::b;
>     export using t::c;  // { dg-error "does not have external linkage" }
> +  export using t::d;  // { dg-error "does not have external linkage" }
> +  export using t::e;  // { dg-error "does not have external linkage" }
>   }
> diff --git a/gcc/testsuite/g++.dg/modules/using-27.C b/gcc/testsuite/g++.dg/modules/using-27.C
> new file mode 100644
> index 00000000000..857d7d4a035
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/using-27.C
> @@ -0,0 +1,14 @@
> +// { dg-additional-options "-fmodules-ts -Wno-global-module" }
> +// { dg-module-cmi !bad }
> +
> +module;
> +
> +static int x = 123;   // { dg-message "declared here with internal linkage" }
> +static void f() {}    // { dg-message "declared here with internal linkage" }
> +using T = struct {};  // { dg-message "declared here with no linkage" }
> +
> +export module bad;
> +
> +export using ::x;  // { dg-error "does not have external linkage" }
> +export using ::f;  // { dg-error "does not have external linkage" }
> +export using ::T;  // { dg-error "does not have external linkage" }
> diff --git a/gcc/testsuite/g++.dg/modules/using-28_a.C b/gcc/testsuite/g++.dg/modules/using-28_a.C
> new file mode 100644
> index 00000000000..96bbef57f64
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/using-28_a.C
> @@ -0,0 +1,12 @@
> +// { dg-additional-options "-fmodules-ts -Wno-global-module" }
> +// { dg-module-cmi M }
> +// Test that typedef names correctly provide external linkage
> +
> +module;
> +typedef struct { int x; } A;
> +export module M;
> +
> +export typedef struct {} B;
> +
> +export using ::A;
> +export using ::B;
> diff --git a/gcc/testsuite/g++.dg/modules/using-28_b.C b/gcc/testsuite/g++.dg/modules/using-28_b.C
> new file mode 100644
> index 00000000000..72876b517ca
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/using-28_b.C
> @@ -0,0 +1,8 @@
> +// { dg-additional-options "-fmodules-ts" }
> +
> +import M;
> +
> +int main() {
> +  A a { 10 };
> +  B b;
> +}
> diff --git a/gcc/testsuite/g++.dg/modules/using-29.H b/gcc/testsuite/g++.dg/modules/using-29.H
> new file mode 100644
> index 00000000000..ea44e0a78cc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/using-29.H
> @@ -0,0 +1,6 @@
> +// { dg-additional-options "-fmodule-header" }
> +
> +static int foo = 123;
> +namespace ns {
> +  using ::foo;  // OK, we're in a header unit
> +}
diff mbox series

Patch

diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index c0f89f98d87..cbb2827808f 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -5206,38 +5206,43 @@  pushdecl_outermost_localscope (tree x)
 static bool
 check_can_export_using_decl (tree binding)
 {
-  tree decl = STRIP_TEMPLATE (binding);
-
-  /* Linkage is determined by the owner of an enumerator.  */
-  if (TREE_CODE (decl) == CONST_DECL)
-    decl = TYPE_NAME (DECL_CONTEXT (decl));
-
-  /* If the using decl is exported, the things it refers
-     to must also be exported (or not have module attachment).  */
-  if (!DECL_MODULE_EXPORT_P (decl)
-      && (DECL_LANG_SPECIFIC (decl)
-	  && DECL_MODULE_ATTACH_P (decl)))
+  /* We want the linkage of the underlying entity, so strip typedefs.
+     If the underlying entity is a builtin type then we're OK.  */
+  tree entity = binding;
+  if (TREE_CODE (entity) == TYPE_DECL)
     {
-      bool internal_p = !TREE_PUBLIC (decl);
+      entity = TYPE_MAIN_DECL (TREE_TYPE (entity));
+      if (!entity)
+	return true;
+    }
+
+  linkage_kind linkage = decl_linkage (entity);
+  tree not_tmpl = STRIP_TEMPLATE (entity);
 
-      /* A template in an anonymous namespace doesn't constrain TREE_PUBLIC
-	 until it's instantiated, so double-check its context.  */
-      if (!internal_p && TREE_CODE (binding) == TEMPLATE_DECL)
-	internal_p = decl_internal_context_p (decl);
+  /* Attachment is determined by the owner of an enumerator.  */
+  if (TREE_CODE (not_tmpl) == CONST_DECL)
+    not_tmpl = TYPE_NAME (DECL_CONTEXT (not_tmpl));
 
+  /* If the using decl is exported, the things it refers to must
+     have external linkage.  decl_linkage returns lk_external for
+     module linkage so also check for attachment.  */
+  if (linkage != lk_external
+      || (DECL_LANG_SPECIFIC (not_tmpl)
+	  && DECL_MODULE_ATTACH_P (not_tmpl)
+	  && !DECL_MODULE_EXPORT_P (not_tmpl)))
+    {
       auto_diagnostic_group d;
       error ("exporting %q#D that does not have external linkage",
 	     binding);
-      if (TREE_CODE (decl) == TYPE_DECL && !DECL_IMPLICIT_TYPEDEF_P (decl))
-	/* An un-exported explicit type alias has no linkage.  */
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with no linkage", binding);
-      else if (internal_p)
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with internal linkage", binding);
+      if (linkage == lk_none)
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with no linkage", entity);
+      else if (linkage == lk_internal)
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with internal linkage", entity);
       else
-	inform (DECL_SOURCE_LOCATION (binding),
-		"%q#D declared here with module linkage", binding);
+	inform (DECL_SOURCE_LOCATION (entity),
+		"%q#D declared here with module linkage", entity);
       return false;
     }
 
diff --git a/gcc/testsuite/g++.dg/modules/using-10.C b/gcc/testsuite/g++.dg/modules/using-10.C
index d468a36f5d8..6f82b5dd147 100644
--- a/gcc/testsuite/g++.dg/modules/using-10.C
+++ b/gcc/testsuite/g++.dg/modules/using-10.C
@@ -23,6 +23,13 @@  namespace s {
   }
 }
 
+export using s::a1;  // { dg-error "does not have external linkage" }
+export using s::b1;  // { dg-error "does not have external linkage" }
+export using s::x1;  // { dg-error "does not have external linkage" }
+export using s::y1;  // { dg-error "does not have external linkage" }
+export using s::f1;  // { dg-error "does not have external linkage" }
+export using s::g1;  // { dg-error "does not have external linkage" }
+
 // module linkage
 namespace m {
   struct a2 {};  // { dg-message "declared here with module linkage" }
@@ -41,13 +48,6 @@  namespace m {
   void g2();  // { dg-message "declared here with module linkage" }
 }
 
-export using s::a1;  // { dg-error "does not have external linkage" }
-export using s::b1;  // { dg-error "does not have external linkage" }
-export using s::x1;  // { dg-error "does not have external linkage" }
-export using s::y1;  // { dg-error "does not have external linkage" }
-export using s::f1;  // { dg-error "does not have external linkage" }
-export using s::g1;  // { dg-error "does not have external linkage" }
-
 export using m::a2;  // { dg-error "does not have external linkage" }
 export using m::b2;  // { dg-error "does not have external linkage" }
 export using m::x2;  // { dg-error "does not have external linkage" }
@@ -55,15 +55,47 @@  export using m::y2;  // { dg-error "does not have external linkage" }
 export using m::f2;  // { dg-error "does not have external linkage" }
 export using m::g2;  // { dg-error "does not have external linkage" }
 
+// no linkage
+namespace n {
+  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
+
+  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
+  using b3 = decltype(tmp_s);
+
+  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
+  using c3 = decltype(tmp_e);
+
+  auto foo() {
+    struct s {};  // { dg-message "declared here with no linkage" }
+    return s{};
+  }
+  using d3 = decltype(foo());
+}
+
+export using n::a3;  // { dg-error "does not have external linkage" }
+export using n::b3;  // { dg-error "does not have external linkage" }
+export using n::c3;  // { dg-error "does not have external linkage" }
+export using n::d3;  // { dg-error "does not have external linkage" }
+
+// typedefs
 namespace t {
-  using a = int;  // { dg-message "declared here with no linkage" }
+  // aliases have the linkage of the entity they ultimately refer to
+  using a = int;
+  typedef a b;
 
+  // a template is not an alias
   template <typename T>
-  using b = int;  // { dg-message "declared here with no linkage" }
+  using c = int;  // { dg-message "declared here with module linkage" }
+
+  // anonymous type with typedef name for linkage purposes
+  typedef struct {} d;  // { dg-message "declared here with module linkage" }
 
-  typedef int c;  // { dg-message "declared here with no linkage" }
+  // non-empty enum gets linkage of enumerator name
+  enum { X } e;  // { dg-message "declared here with module linkage"} 
 }
 
-export using t::a;  // { dg-error "does not have external linkage" }
-export using t::b;  // { dg-error "does not have external linkage" }
+export using t::a;
+export using t::b;
 export using t::c;  // { dg-error "does not have external linkage" }
+export using t::d;  // { dg-error "does not have external linkage" }
+export using t::e;  // { dg-error "does not have external linkage" }
diff --git a/gcc/testsuite/g++.dg/modules/using-12.C b/gcc/testsuite/g++.dg/modules/using-12.C
index 52ef3c6285d..4fd71696f62 100644
--- a/gcc/testsuite/g++.dg/modules/using-12.C
+++ b/gcc/testsuite/g++.dg/modules/using-12.C
@@ -57,15 +57,47 @@  namespace m {
   export using m::g2;  // { dg-error "does not have external linkage" }
 }
 
+// no linkage
+namespace n {
+  using a3 = struct { int x; };  // { dg-message "declared here with no linkage" }
+
+  struct {} tmp_s;  // { dg-message "declared here with no linkage" }
+  using b3 = decltype(tmp_s);
+
+  enum {} tmp_e;  // { dg-message "declared here with no linkage" }
+  using c3 = decltype(tmp_e);
+
+  auto foo() {
+    struct s {};  // { dg-message "declared here with no linkage" }
+    return s{};
+  }
+  using d3 = decltype(foo());
+
+  export using n::a3;  // { dg-error "does not have external linkage" }
+  export using n::b3;  // { dg-error "does not have external linkage" }
+  export using n::c3;  // { dg-error "does not have external linkage" }
+  export using n::d3;  // { dg-error "does not have external linkage" }
+}
+
+// typedefs
 namespace t {
-  using a = int;  // { dg-message "declared here with no linkage" }
+  // aliases have the linkage of the entity they ultimately refer to
+  using a = int;
+  typedef a b;
 
+  // a template is not an alias
   template <typename T>
-  using b = int;  // { dg-message "declared here with no linkage" }
+  using c = int;  // { dg-message "declared here with module linkage" }
+
+  // anonymous type with typedef name for linkage purposes
+  typedef struct {} d;  // { dg-message "declared here with module linkage" }
 
-  typedef int c;  // { dg-message "declared here with no linkage" }
+  // non-empty enum gets linkage of enumerator name
+  enum { X } e;  // { dg-message "declared here with module linkage" }
 
-  export using t::a;  // { dg-error "does not have external linkage" }
-  export using t::b;  // { dg-error "does not have external linkage" }
+  export using t::a;
+  export using t::b;
   export using t::c;  // { dg-error "does not have external linkage" }
+  export using t::d;  // { dg-error "does not have external linkage" }
+  export using t::e;  // { dg-error "does not have external linkage" }
 }
diff --git a/gcc/testsuite/g++.dg/modules/using-27.C b/gcc/testsuite/g++.dg/modules/using-27.C
new file mode 100644
index 00000000000..857d7d4a035
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-27.C
@@ -0,0 +1,14 @@ 
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi !bad }
+
+module;
+
+static int x = 123;   // { dg-message "declared here with internal linkage" }
+static void f() {}    // { dg-message "declared here with internal linkage" }
+using T = struct {};  // { dg-message "declared here with no linkage" }
+
+export module bad;
+
+export using ::x;  // { dg-error "does not have external linkage" }
+export using ::f;  // { dg-error "does not have external linkage" }
+export using ::T;  // { dg-error "does not have external linkage" }
diff --git a/gcc/testsuite/g++.dg/modules/using-28_a.C b/gcc/testsuite/g++.dg/modules/using-28_a.C
new file mode 100644
index 00000000000..96bbef57f64
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-28_a.C
@@ -0,0 +1,12 @@ 
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi M }
+// Test that typedef names correctly provide external linkage
+
+module;
+typedef struct { int x; } A;
+export module M;
+
+export typedef struct {} B;
+
+export using ::A;
+export using ::B;
diff --git a/gcc/testsuite/g++.dg/modules/using-28_b.C b/gcc/testsuite/g++.dg/modules/using-28_b.C
new file mode 100644
index 00000000000..72876b517ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/using-28_b.C
@@ -0,0 +1,8 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+int main() {
+  A a { 10 };
+  B b;
+}