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 <[email protected]> --- 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 } +}
