On Sat, Mar 07, 2026 at 05:38:01PM +1100, Nathaniel Shead wrote:
> While testing the fix for PR124311 I noticed that we are missing a
> diagnostic for this case.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> Or otherwise 17?
> 

I'd accidentally tested a different version.  Here's an updated version
with more tests and fixing a mistake where we missed member functions
that don't go through start_decl.

Bootstrapped and regtestd on x86_64-pc-linux-gnu, OK for trunk?
But this is not a regression, so otherwise for 17?

-- >8 --

We had missed checking whether we were doing an (outside class)
declaration of a member attached to the wrong module.  This patch adds
those checks.

Within duplicate_decls/maybe_module_redeclare there's not a nice way to
do this because the attachment of a member is always calculated based on
the attachment of its owning type, which defeats the purpose here.
Instead I added the checks to start_decl and grokfndecl where we do
other various checks for if a redeclaration of a class member is legal.

gcc/cp/ChangeLog:

        * decl.cc (start_decl): Check module_may_redeclare for members.
        (grokfndecl): Likewise.

gcc/testsuite/ChangeLog:

        * g++.dg/modules/class-12_a.C: New test.
        * g++.dg/modules/class-12_b.C: New test.
        * g++.dg/modules/class-12_c.C: New test.

Signed-off-by: Nathaniel Shead <[email protected]>
---
 gcc/cp/decl.cc                            |  7 ++++
 gcc/testsuite/g++.dg/modules/class-12_a.C | 44 ++++++++++++++++++++
 gcc/testsuite/g++.dg/modules/class-12_b.C | 49 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/modules/class-12_c.C | 48 ++++++++++++++++++++++
 4 files changed, 148 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/modules/class-12_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/class-12_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/class-12_c.C

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 6b210a30b6a..e0a2074d2d1 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -6693,6 +6693,10 @@ start_decl (const cp_declarator *declarator,
                               context, DECL_NAME (decl));
                  DECL_CONTEXT (decl) = DECL_CONTEXT (field);
                }
+
+             if (modules_p () && !module_may_redeclare (field))
+               return error_mark_node;
+
              /* Static data member are tricky; an in-class initialization
                 still doesn't provide a definition, so the in-class
                 declaration will have DECL_EXTERNAL set, but will have an
@@ -12678,6 +12682,9 @@ grokfndecl (tree ctype,
              return NULL_TREE;
            }
 
+         if (modules_p () && !module_may_redeclare (old_decl))
+           return NULL_TREE;
+
          /* Since we've smashed OLD_DECL to its
             DECL_TEMPLATE_RESULT, we must do the same to DECL.  */
          if (TREE_CODE (decl) == TEMPLATE_DECL)
diff --git a/gcc/testsuite/g++.dg/modules/class-12_a.C 
b/gcc/testsuite/g++.dg/modules/class-12_a.C
new file mode 100644
index 00000000000..15b2a2b81f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/class-12_a.C
@@ -0,0 +1,44 @@
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+
+export module M;
+
+export extern "C++" struct Global {
+  Global();
+  Global(int);
+  void foo();
+  static int x;
+  struct S;
+  template <typename> void a();
+  template <typename> static int r;
+  template <typename> struct X;
+
+  Global(const Global&);
+  Global(double);
+  void bar();
+  static int y;
+  struct T;
+  template <typename> void b();
+  template <typename> static int s;
+  template <typename> struct Y;
+};
+
+export struct Module {
+  Module();
+  Module(int);
+  void foo();
+  static int x;
+  struct S;
+  template <typename> void a();
+  template <typename> static int r;
+  template <typename> struct X;
+
+  Module(const Module&);
+  Module(double);
+  void bar();
+  static int y;
+  struct T;
+  template <typename> void b();
+  template <typename> static int s;
+  template <typename> struct Y;
+};
diff --git a/gcc/testsuite/g++.dg/modules/class-12_b.C 
b/gcc/testsuite/g++.dg/modules/class-12_b.C
new file mode 100644
index 00000000000..559be1e7090
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/class-12_b.C
@@ -0,0 +1,49 @@
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi !X }
+
+export module X;
+import M;
+
+Global::Global() = default; // { dg-error "in module .X. conflicts with 
import" }
+Global::Global(int) {}     // { dg-error "in module .X. conflicts with import" 
}
+void Global::foo() {}      // { dg-error "in module .X. conflicts with import" 
}
+int Global::x;             // { dg-error "in module .X. conflicts with import" 
}
+struct Global::S {};       // { dg-error "in module .X. conflicts with import" 
}
+
+template <typename> void Global::a() {}          // { dg-error "in module .X. 
conflicts with import" }
+template <typename> int Global::r {};    // { dg-error "in module .X. 
conflicts with import" }
+template <typename> struct Global::X {};  // { dg-error "in module .X. 
conflicts with import" }
+
+extern "C++" {
+  Global::Global(const Global &) = default;
+  Global::Global(double) {}
+  void Global::bar() {}
+  int Global::y;
+  struct Global::T {};
+
+  template <typename> void Global::b() {}
+  template <typename> int Global::s {};
+  template <typename> struct Global::Y {};
+}
+
+Module::Module() = default; // { dg-error "in module .X. conflicts with 
import" }
+Module::Module(int) {}     // { dg-error "in module .X. conflicts with import" 
}
+void Module::foo() {}      // { dg-error "in module .X. conflicts with import" 
}
+int Module::x;             // { dg-error "in module .X. conflicts with import" 
}
+struct Module::S {};       // { dg-error "in module .X. conflicts with import" 
}
+
+template <typename> void Module::a() {}          // { dg-error "in module .X. 
conflicts with import" }
+template <typename> int Module::r {};    // { dg-error "in module .X. 
conflicts with import" }
+template <typename> struct Module::X {};  // { dg-error "in module .X. 
conflicts with import" }
+
+extern "C++" {
+  Module::Module(const Module &) = default; // { dg-error "in global module 
conflicts with import" }
+  Module::Module(double) {}                // { dg-error "in global module 
conflicts with import" }
+  void Module::bar() {}                            // { dg-error "in global 
module conflicts with import" }
+  int Module::y;                           // { dg-error "in global module 
conflicts with import" }
+  struct Module::T {};                     // { dg-error "in global module 
conflicts with import" }
+
+  template <typename> void Module::b() {}   // { dg-error "in global module 
conflicts with import" }
+  template <typename> int Module::s {};            // { dg-error "in global 
module conflicts with import" }
+  template <typename> struct Module::Y {};  // { dg-error "in global module 
conflicts with import" }
+}
diff --git a/gcc/testsuite/g++.dg/modules/class-12_c.C 
b/gcc/testsuite/g++.dg/modules/class-12_c.C
new file mode 100644
index 00000000000..8f0d0bf7bd4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/class-12_c.C
@@ -0,0 +1,48 @@
+// { dg-additional-options "-fmodules" }
+
+module M:part;
+import M;
+
+Global::Global() = default; // { dg-error "in module .M. conflicts with 
import" }
+Global::Global(int) {}     // { dg-error "in module .M. conflicts with import" 
}
+void Global::foo() {}      // { dg-error "in module .M. conflicts with import" 
}
+int Global::x;             // { dg-error "in module .M. conflicts with import" 
}
+struct Global::S {};       // { dg-error "in module .M. conflicts with import" 
}
+
+template <typename> void Global::a() {}          // { dg-error "in module .M. 
conflicts with import" }
+template <typename> int Global::r {};    // { dg-error "in module .M. 
conflicts with import" }
+template <typename> struct Global::X {};  // { dg-error "in module .M. 
conflicts with import" }
+
+extern "C++" {
+  Global::Global(const Global &) = default;
+  Global::Global(double) {}
+  void Global::bar() {}
+  int Global::y;
+  struct Global::T {};
+
+  template <typename> void Global::b() {}
+  template <typename> int Global::s {};
+  template <typename> struct Global::Y {};
+}
+
+Module::Module() = default;
+Module::Module(int) {}
+void Module::foo() {}
+int Module::x;
+struct Module::S {};
+
+template <typename> void Module::a() {}
+template <typename> int Module::r {};
+template <typename> struct Module::X{};
+
+extern "C++" {
+  Module::Module(const Module &) = default; // { dg-error "in global module 
conflicts with import" }
+  Module::Module(double) {}                // { dg-error "in global module 
conflicts with import" }
+  void Module::bar() {}                            // { dg-error "in global 
module conflicts with import" }
+  int Module::y;                           // { dg-error "in global module 
conflicts with import" }
+  struct Module::T {};                     // { dg-error "in global module 
conflicts with import" }
+
+  template <typename> void Module::b() {}   // { dg-error "in global module 
conflicts with import" }
+  template <typename> int Module::s {};            // { dg-error "in global 
module conflicts with import" }
+  template <typename> struct Module::Y {};  // { dg-error "in global module 
conflicts with import" }
+}
-- 
2.51.0

Reply via email to