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