On 8/27/25 8:29 AM, Nathaniel Shead wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
OK.
-- >8 --
The confusion in the PR arose because the definition of 'User' in a
separate named module did not provide an implementation for the
forward-declaration in the global module. This seems likely to be a
common mistake while people are transitioning to modules, so this patch
adds an explanatory note.
PR c++/119844
gcc/cp/ChangeLog:
* typeck2.cc (cxx_incomplete_type_inform): Add explanation when
a similar type is complete but attached to a different module.
gcc/testsuite/ChangeLog:
* g++.dg/modules/pr119844_a.C: New test.
* g++.dg/modules/pr119844_b.C: New test.
Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
---
gcc/cp/typeck2.cc | 30 ++++++++++++++++++++---
gcc/testsuite/g++.dg/modules/pr119844_a.C | 9 +++++++
gcc/testsuite/g++.dg/modules/pr119844_b.C | 14 +++++++++++
3 files changed, 50 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/modules/pr119844_a.C
create mode 100644 gcc/testsuite/g++.dg/modules/pr119844_b.C
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index faaf1df6158..0c02b678256 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -286,10 +286,34 @@ cxx_incomplete_type_inform (const_tree type)
&& same_type_p (ptype, current_class_type))
inform (loc, "definition of %q#T is not complete until "
"the closing brace", ptype);
- else if (!TYPE_TEMPLATE_INFO (ptype))
- inform (loc, "forward declaration of %q#T", ptype);
else
- inform (loc, "declaration of %q#T", ptype);
+ {
+ if (!TYPE_TEMPLATE_INFO (ptype))
+ inform (loc, "forward declaration of %q#T", ptype);
+ else
+ inform (loc, "declaration of %q#T", ptype);
+
+ /* If there's a similar-looking complete type attached
+ to a different module, point at that as a suggestion. */
+ if (modules_p () && TYPE_NAMESPACE_SCOPE_P (ptype))
+ {
+ tree decl = TYPE_NAME (ptype);
+ tree result = lookup_qualified_name (CP_DECL_CONTEXT (decl),
+ DECL_NAME (decl),
+ LOOK_want::TYPE);
+ if (TREE_CODE (result) == TREE_LIST)
+ for (; result; result = TREE_CHAIN (result))
+ {
+ tree cand = TREE_VALUE (result);
+ if (!COMPLETE_TYPE_P (TREE_TYPE (cand)))
+ continue;
+ inform (DECL_SOURCE_LOCATION (cand),
+ "%q#T is complete but does not correspond with %q#T "
+ "because it is attached to a different module",
+ TREE_TYPE (cand), ptype);
+ }
+ }
+ }
}
/* Print an error message for invalid use of an incomplete type.
diff --git a/gcc/testsuite/g++.dg/modules/pr119844_a.C
b/gcc/testsuite/g++.dg/modules/pr119844_a.C
new file mode 100644
index 00000000000..bac57fa1a94
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pr119844_a.C
@@ -0,0 +1,9 @@
+// PR c++/119844
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+
+export module M;
+
+export struct User {
+ int value;
+};
diff --git a/gcc/testsuite/g++.dg/modules/pr119844_b.C
b/gcc/testsuite/g++.dg/modules/pr119844_b.C
new file mode 100644
index 00000000000..9530170c734
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pr119844_b.C
@@ -0,0 +1,14 @@
+// PR c++/119844
+// { dg-additional-options "-fmodules" }
+
+struct User; // { dg-message "declaration" }
+struct MainWindow {
+ User* u;
+};
+
+import M;
+
+int foo(MainWindow m) {
+ return m.u->value; // { dg-error "incomplete" }
+ // { dg-message "'User@M' is complete" "" { target *-*-* } 0 }
+}