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 }
+}

Reply via email to