Message ID | 6646f5cb.170a0220.fb17d.75a5@mx.google.com |
---|---|
State | New |
Headers | show |
Series | [v2] c++/modules: Remember that header units have CMIs | expand |
On Fri, May 17, 2024 at 04:14:31PM +1000, Nathaniel Shead wrote: > On Tue, May 14, 2024 at 06:21:48PM -0400, Jason Merrill wrote: > > On 5/12/24 22:58, Nathaniel Shead wrote: > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > > > OK. > > > > I realised as I was looking over this again that I might have spoken too > soon with the header unit example being supported. Doing the following: > > // a.H > struct { int y; } s; > decltype(s) f(decltype(s)); // { dg-error "used but never defined" } > inline auto x = f({ 123 }); > > // b.C > struct {} unrelated; > import "a.H"; > decltype(s) f(decltype(s) x) { > return { 456 + x.y }; > } > > // c.C > import "linkage-3_a.H"; > int main() { auto a = x.y; } > > Actually does fail to link, because in 'c.C' we call 'f(.anon_0)' but > the definition 'b.C' is f(.anon_1). > > I don't think this is fixable, so I don't think this direction is > workable. > > That said, I think that it might still be worth making header modules > satisfy 'module_has_cmi_p', since that is true to the name, and will be > useful in other places we currently use 'module_p ()': in which case we > could instead make all the callers in 'no_linkage_check' do > 'module_maybe_has_cmi_p () && !header_module_p ()'; something like the > following, perhaps? > > But I'm not too fussed about this overall if you think this will just > make things more complicated. Otherwise bootstrapped and regtested (so > far just modules.exp) on x86_64-pc-linux-gnu, OK for trunk if full > regtest passes? > > -- >8 -- > > This appears to be an oversight in the definition of module_has_cmi_p. > This change will allow us to use the function directly in more places > that need to additional work only if generating a module CMI in the > future. > > However, we do need to change callers of 'module_maybe_has_cmi_p'; in > particular header units, though having a CMI, do not provide a TU to > emit names into, and thus each importer will emit their own definitions > which may not match for no-linkage types. > > gcc/cp/ChangeLog: > > * cp-tree.h (module_has_cmi_p): Also true for header units. > * decl.cc (grokfndecl): Disallow no-linkage names in header > units. > * tree.cc (no_linkage_check): Likewise. > > gcc/testsuite/ChangeLog: > > * g++.dg/modules/linkage-3.H: New test. > > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> > --- > gcc/cp/cp-tree.h | 2 +- > gcc/cp/decl.cc | 2 +- > gcc/cp/tree.cc | 13 +++++++----- > gcc/testsuite/g++.dg/modules/linkage-3.H | 25 ++++++++++++++++++++++++ > 4 files changed, 35 insertions(+), 7 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/modules/linkage-3.H > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index ba9e848c177..ac55b5579a1 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -7381,7 +7381,7 @@ inline bool module_interface_p () > inline bool module_partition_p () > { return module_kind & MK_PARTITION; } > inline bool module_has_cmi_p () > -{ return module_kind & (MK_INTERFACE | MK_PARTITION); } > +{ return module_kind & (MK_INTERFACE | MK_PARTITION | MK_HEADER); } > > inline bool module_purview_p () > { return module_kind & MK_PURVIEW; } > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index 6fcab615d55..f89a7df30b7 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -10802,7 +10802,7 @@ grokfndecl (tree ctype, > used by an importer. We don't just use module_has_cmi_p here > because for entities in the GMF we don't yet know whether this > module will have a CMI, so we'll conservatively assume it might. */ > - publicp = module_maybe_has_cmi_p (); > + publicp = module_maybe_has_cmi_p () && !header_module_p (); > > if (publicp && cxx_dialect == cxx98) > { > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 9d37d255d8d..00c50e3130d 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -2974,9 +2974,9 @@ verify_stmt_tree (tree t) > > /* Check if the type T depends on a type with no linkage and if so, > return it. If RELAXED_P then do not consider a class type declared > - within a vague-linkage function or in a module CMI to have no linkage, > - since it can still be accessed within a different TU. Remember: > - no-linkage is not the same as internal-linkage. */ > + within a vague-linkage function or in a non-header module CMI to > + have no linkage, since it can still be accessed within a different TU. > + Remember: no-linkage is not the same as internal-linkage. */ > > tree > no_linkage_check (tree t, bool relaxed_p) > @@ -3019,7 +3019,8 @@ no_linkage_check (tree t, bool relaxed_p) > { > if (relaxed_p > && TREE_PUBLIC (CP_TYPE_CONTEXT (t)) > - && module_maybe_has_cmi_p ()) > + && module_maybe_has_cmi_p () > + && !header_module_p ()) > /* This type could possibly be accessed outside this TU. */ > return NULL_TREE; > else > @@ -3037,7 +3038,9 @@ no_linkage_check (tree t, bool relaxed_p) > { > if (relaxed_p > && (vague_linkage_p (r) > - || (TREE_PUBLIC (r) && module_maybe_has_cmi_p ()))) > + || (TREE_PUBLIC (r) > + && module_maybe_has_cmi_p () > + && !header_module_p ()))) > r = CP_DECL_CONTEXT (r); > else > return t; > diff --git a/gcc/testsuite/g++.dg/modules/linkage-3.H b/gcc/testsuite/g++.dg/modules/linkage-3.H > new file mode 100644 > index 00000000000..a34ff084eaf > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/linkage-3.H > @@ -0,0 +1,25 @@ > +// { dg-additional-options "-fmodule-header" } > +// { dg-module-cmi !{} } > + > +// Like linkage-1, but for header units. > + > +// External linkage definitions must be declared as 'inline' to satisfy > +// [module.import] p6, so we don't need to care about voldemort types in > +// function definitions. However, we still care about anonymous types like > +// this: because a header unit is not a TU, it's up to each importer to export > +// the name declared here, and depending on what other anonymous types they > +// declare they could give each declaration different mangled names. > +// So we should still complain about this because in general it's not safe > +// to assume that the declaration can be provided in another TU; this is OK > +// to do by [module.import] p5. > + > +inline auto f() { > + struct A {}; > + return A{}; > +} > +decltype(f()) g(); // OK, vague linkage function > +auto x = g(); > + > +struct { int y; } s; > +decltype(s) h(); // { dg-error "used but never defined" } > +inline auto y = h(); > -- > 2.43.2 > Oops, I attached the wrong version of the test: it should be this one: diff --git a/gcc/testsuite/g++.dg/modules/linkage-3.H b/gcc/testsuite/g++.dg/modules/linkage-3.H new file mode 100644 index 00000000000..3e1b4bad057 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/linkage-3.H @@ -0,0 +1,26 @@ +// { dg-additional-options "-fmodule-header -Wno-error=c++20-extensions" } +// { dg-module-cmi !{} } + +// Like linkage-1, but for header units. + +// External linkage definitions must be declared as 'inline' to satisfy +// [module.import] p6, so we don't need to care about voldemort types in +// function definitions. However, we still care about anonymous types like +// this: because a header unit is not a TU, it's up to each importer to export +// the name declared here, and depending on what other anonymous types they +// declare they could give each declaration different mangled names. +// So we should still complain about this because in general it's not safe +// to assume that the declaration can be provided in another TU; this is OK +// to do by [module.import] p5. + +// OK, vague linkage function +inline auto f() { + struct A {}; + return A{}; +} +decltype(f()) g(); // { dg-warning "used but not defined" "" { target c++17_down } } +auto x = g(); + +struct { int y; } s; +decltype(s) h(); // { dg-error "used but never defined" } +inline auto y = h();
On 5/17/24 02:14, Nathaniel Shead wrote: > On Tue, May 14, 2024 at 06:21:48PM -0400, Jason Merrill wrote: >> On 5/12/24 22:58, Nathaniel Shead wrote: >>> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? >> >> OK. >> > > I realised as I was looking over this again that I might have spoken too > soon with the header unit example being supported. Doing the following: > > // a.H > struct { int y; } s; > decltype(s) f(decltype(s)); // { dg-error "used but never defined" } > inline auto x = f({ 123 }); > > // b.C > struct {} unrelated; > import "a.H"; > decltype(s) f(decltype(s) x) { > return { 456 + x.y }; > } > > // c.C > import "linkage-3_a.H"; > int main() { auto a = x.y; } > > Actually does fail to link, because in 'c.C' we call 'f(.anon_0)' but > the definition 'b.C' is f(.anon_1). > > I don't think this is fixable, so I don't think this direction is > workable. Since namespace-scope anonymous types are TU-local, we don't need to support that for proper modules, but it's not clear to me that we don't need to support it for header units. OTOH, https://eel.is/c++draft/module#import-5.3 allows c.C to import a different header unit than b.C, in which case the type is different and x violates the odr. > That said, I think that it might still be worth making header modules > satisfy 'module_has_cmi_p', since that is true to the name, and will be > useful in other places we currently use 'module_p ()': in which case we > could instead make all the callers in 'no_linkage_check' do > 'module_maybe_has_cmi_p () && !header_module_p ()'; something like the > following, perhaps? If we need that condition, it should be its own predicate rather than expecting callers to do that combined check. But it's not clear to me how this is different from a type in the GMF of a named module, which is exactly the maybe_has_cmi case; there we could again see a different version of the type if another TU includes the header. Jason
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ba9e848c177..ac55b5579a1 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7381,7 +7381,7 @@ inline bool module_interface_p () inline bool module_partition_p () { return module_kind & MK_PARTITION; } inline bool module_has_cmi_p () -{ return module_kind & (MK_INTERFACE | MK_PARTITION); } +{ return module_kind & (MK_INTERFACE | MK_PARTITION | MK_HEADER); } inline bool module_purview_p () { return module_kind & MK_PURVIEW; } diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 6fcab615d55..f89a7df30b7 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10802,7 +10802,7 @@ grokfndecl (tree ctype, used by an importer. We don't just use module_has_cmi_p here because for entities in the GMF we don't yet know whether this module will have a CMI, so we'll conservatively assume it might. */ - publicp = module_maybe_has_cmi_p (); + publicp = module_maybe_has_cmi_p () && !header_module_p (); if (publicp && cxx_dialect == cxx98) { diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 9d37d255d8d..00c50e3130d 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -2974,9 +2974,9 @@ verify_stmt_tree (tree t) /* Check if the type T depends on a type with no linkage and if so, return it. If RELAXED_P then do not consider a class type declared - within a vague-linkage function or in a module CMI to have no linkage, - since it can still be accessed within a different TU. Remember: - no-linkage is not the same as internal-linkage. */ + within a vague-linkage function or in a non-header module CMI to + have no linkage, since it can still be accessed within a different TU. + Remember: no-linkage is not the same as internal-linkage. */ tree no_linkage_check (tree t, bool relaxed_p) @@ -3019,7 +3019,8 @@ no_linkage_check (tree t, bool relaxed_p) { if (relaxed_p && TREE_PUBLIC (CP_TYPE_CONTEXT (t)) - && module_maybe_has_cmi_p ()) + && module_maybe_has_cmi_p () + && !header_module_p ()) /* This type could possibly be accessed outside this TU. */ return NULL_TREE; else @@ -3037,7 +3038,9 @@ no_linkage_check (tree t, bool relaxed_p) { if (relaxed_p && (vague_linkage_p (r) - || (TREE_PUBLIC (r) && module_maybe_has_cmi_p ()))) + || (TREE_PUBLIC (r) + && module_maybe_has_cmi_p () + && !header_module_p ()))) r = CP_DECL_CONTEXT (r); else return t; diff --git a/gcc/testsuite/g++.dg/modules/linkage-3.H b/gcc/testsuite/g++.dg/modules/linkage-3.H new file mode 100644 index 00000000000..a34ff084eaf --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/linkage-3.H @@ -0,0 +1,25 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi !{} } + +// Like linkage-1, but for header units. + +// External linkage definitions must be declared as 'inline' to satisfy +// [module.import] p6, so we don't need to care about voldemort types in +// function definitions. However, we still care about anonymous types like +// this: because a header unit is not a TU, it's up to each importer to export +// the name declared here, and depending on what other anonymous types they +// declare they could give each declaration different mangled names. +// So we should still complain about this because in general it's not safe +// to assume that the declaration can be provided in another TU; this is OK +// to do by [module.import] p5. + +inline auto f() { + struct A {}; + return A{}; +} +decltype(f()) g(); // OK, vague linkage function +auto x = g(); + +struct { int y; } s; +decltype(s) h(); // { dg-error "used but never defined" } +inline auto y = h();