-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][modules] HeaderSearch::MarkFileModuleHeader sets textual headers' HeaderFileInfo non-external when it shouldn't #89005
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang Author: Ian Anderson (ian-twilightcoder) ChangesHeaderSearch::MarkFileModuleHeader is no longer properly checking for no-changes, and so creates a new HeaderFileInfo for every Full diff: https://github.com/llvm/llvm-project/pull/89005.diff 1 Files Affected:
diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index 0632882b296146..9409b97ba0e64a 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -1313,6 +1313,14 @@ OptionalFileEntryRef HeaderSearch::LookupSubframeworkHeader(
// File Info Management.
//===----------------------------------------------------------------------===//
+static bool
+headerFileInfoModuleBitsMatchRole(const HeaderFileInfo *HFI,
+ ModuleMap::ModuleHeaderRole Role) {
+ return (HFI->isModuleHeader == ModuleMap::isModular(Role)) &&
+ (HFI->isTextualModuleHeader ==
+ ((Role & ModuleMap::TextualHeader) != 0));
+}
+
static void mergeHeaderFileInfoModuleBits(HeaderFileInfo &HFI,
bool isModuleHeader,
bool isTextualModuleHeader) {
@@ -1432,7 +1440,7 @@ void HeaderSearch::MarkFileModuleHeader(FileEntryRef FE,
if ((Role & ModuleMap::ExcludedHeader))
return;
auto *HFI = getExistingFileInfo(FE);
- if (HFI && HFI->isModuleHeader)
+ if (HFI && headerFileInfoModuleBitsMatchRole(HFI, Role))
return;
}
|
I don't really know a great way to write a test for this. If someone could point me at a similar existing test or has an idea how to write a new test that would be much appreciated. |
@ilya-biryukov can you check that this fixes your running out of source location space problem please? |
As a test, maybe you could probe the resulting PCM with |
Just tried it. The patch as is did not help. Changing from |
Until recently that function still consulted |
Does it not help because |
clang/lib/Lex/HeaderSearch.cpp
Outdated
return (HFI->isModuleHeader == ModuleMap::isModular(Role)) && | ||
(HFI->isTextualModuleHeader == | ||
((Role & ModuleMap::TextualHeader) != 0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be:
return (HFI->isModuleHeader == ModuleMap::isModular(Role)) && | |
(HFI->isTextualModuleHeader == | |
((Role & ModuleMap::TextualHeader) != 0)); | |
return (HFI->isModuleHeader == ModuleMap::isModular(Role)) && | |
(HFI->isModuleHeader || | |
HFI->isTextualModuleHeader == | |
((Role & ModuleMap::TextualHeader) != 0)); |
It looks like you're considering "modular header" and "textual header" to be mutually exclusive in HeaderFileInfo
when merging, so presumably we should do the same here. I don't think this would make a difference for our use case, though.
Incidentally, this choice of meaning for isTextualModuleHeader
seems a little confusing to me from a modeling perspective, given that a header can absolutely be modular in one module and textual in another, but I think it's fine from a correctness standpoint. I think it'd be clearer to either use a three-value enum here, or to independently track whether the header is textual or modular and change the check below to isTextualModuleHeader && !isModuleHeader
. But that's not relevant for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see what you're saying. I must have a HFI that says the header is normal-modular, and a role that says it's textual-modular. When I merge in the role it's not going to change the HFI since isModuleHeader
never gets downgraded, but my matching check will return false. Sneaky.
It looks like you're considering "modular header" and "textual header" to be mutually exclusive
Yes, I even put an assert in the merging code. But I'm told asserts aren't usually (ever?) compiled in, so I guess that's probably why it's not getting hit. I did notice a test listing a header as normal in one module and textual in another. What does that actually mean though? The test uses it to exploit a bug in [no_undeclared_includes]
where the compiler will use one module to resolve the include and a different module to check for uses and bypass what the used module says is allowed. Really what does it mean for a header to be in multiple modules at all? Doesn't that just cause non-deterministic behavior in header->module lookup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a file is in multiple modules, we use the logic in findModuleForHeader
and isBetterKnownHeader
to pick between them when deciding how to handle a #include
: prefer the declaration from the current module over anything else, prefer available over non-available, public over private, non-textual over textual, and (if we're allowing excluded headers at all) non-excluded over excluded.
If there's still a tie, then yeah, we make an arbitrary decision. (Not non-deterministic -- we prefer the first-loaded module map -- but also not likely to be predictable or useful. But it should generally not matter which one we pick -- the header should have been parsed and interpreted in the same way regardless -- unless different .pcm
s have different compiler flags, in which case we have an ODR violation.)
The point of textual header
-- at least in our configuration -- is to mark a header as being intentionally part of a module, so that [no_undeclared_includes]
/ -fstrict-modules-decluse
can diagnose a #include
of a file that wasn't a declared dependency. This means that it can be reasonable for a header to be a textual header in one module and a non-textual header in another module -- in particular, that would mean that code with a direct dependency on the first module is permitted to include the header, but that the header is not built as part of building that library. If that compilation is passed a .pcm
containing the same header as a non-textual header, the desired behavior is that we perform an import for that header (treat it as non-textual for #include
handling) but use the textual header
declaration when determining whether the inclusion is permitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I quite understand. I think the test looked something like this.
module A [no_undeclared_includes] {
module one { header "one.h" }
module two { header "two" }
}
module B {
module one { textual header "one.h" }
module three { header "three.h" }
}
That allows one.h to include three.h (and I think anything else?) even though A doesn't have a use B
. But wouldn't the better setup A to have the use B
? I might not be quite understanding what you're saying.
f45cb72
to
0ea2af8
Compare
Ok this one should fix the logic I think. @ilya-biryukov can you give this a try please? |
/// Whether this header is a `textual header` in a module. If a header is | ||
/// textual in one module and normal in another module, this bit will not be | ||
/// set, only `isModuleHeader`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this behavior is weird? Maybe both bits should be set in this scenario? HeaderSearch::ShouldEnterIncludeFile
-> MaybeReenterImportedFile
could change its check to !FileInfo.isModuleHeader && FileInfo.isTextualModuleHeader
What would I be looking for? Presumably the problem is we import the same module twice but fail to find the built pcm in the module cache and so we build it again? I don't know what else would cause runaway growth of pcm files... I actually don't really understand the problem Ilya is hitting... But Richard found (2 now!) good bugs in my code and I'm hoping fixing that will fix Ilya's problem. |
Also note that if (!HFI || (HFI->isModuleHeader && !HFI->isCompilingModuleHeader))
continue; Since textual headers from other modules have |
#83660 shouldn't affect that logic. |
@ilya-biryukov sorry to ping you again, just nobody else knows how to test this. |
Ilya is out on vacation, I'm able to test this and will get started on that now (apologies for delay & thanks for digging into this) |
Unfortunately with this patch I'm still seeing the same source-location-exhausted error. |
Damn I really thought that was going to be the one. If you have any way I could reproduce or better write a test that would be great. |
Can you try applying #89428 as well? I think we would need both in order to prune the module map from the serialized SLocEntries. |
Hmm, I locally reverted #87849 and still see the same issue. In any case I'll try it, and then work on constructing a reproducer (so far I've been mostly treating this as a black box) |
#89441 might be of interest too. |
#89441 fixes our build problems, with or without this patch applied. It's possible this patch makes things better - I haven't checked for actual sloc usage yet, just whether the build fails. So I'll focus on understanding that one and then return here. (I think I'm close to a sharable reproducer too) |
Just so you're aware, it's possible that #87848 introduced some unexpected behavior change. It would be good to check if your issues are caused just by one patch in isolation or by a combination of more patches that landed recently. |
Thanks for the pointer to 87848 - reverting that one locally doesn't help though, even in combination with applying #89005 and #89428. So this change isn't on the critical path to fixing our builds, but still much appreciated and will take a look now. Unsurprisingly, the builds that hit this limit are big and slow :-) Here's a reproducer that seems to capture our case well: modulegen.zip Good
Bad
|
…ers' HeaderFileInfo non-external when it shouldn't HeaderSearch::MarkFileModuleHeader is no longer properly checking for no-changes, and so sets the HeaderFileInfo for every `textual header` to non-external.
0ea2af8
to
45e7409
Compare
From Jan:
|
HeaderSearch::MarkFileModuleHeader is no longer properly checking for no-changes, and so sets the HeaderFileInfo for every
textual header
to non-external.