-
Couldn't load subscription status.
- Fork 10.6k
[Serialization] Report modularization breaks as proper diagnostics #65713
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
Conversation
|
@swift-ci Please smoke test |
0829c68 to
2c43e2b
Compare
|
@swift-ci Please smoke test |
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.
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 like the general direction of providing more complete diagnostics for these failures. My main criticism is that I'm not sure the specific calls to action here are applicable. Suggesting uninstallation of roots may not help because presumably the root is needed. Suggesting auditing headers is a bit vague; I'm not sure what I would personally do to follow through on that suggestion.
If the problem is that the .swiftmodule is stale but the build system/compiler is unable to detect that it ought to rebuild it, maybe what we should be suggesting is to remove the potentially stale .swiftmodule?
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.
Suggesting to delete the stale swiftmodule makes perfect sense, it would fix it in most roots case.
I'm currently looking into pointing the diagnostics on the swiftmodule file with the broken reference instead of the current <unknown>:0:. By itself it should help to put into doubts the SDK or recently installed roots.
The case of auditing headers may be too general to be useful. I'm mostly thinking about the Xcc issue but describing that in a diagnostic is a challenge. I'm considerong moving the recommended solutions to separate notes, so we could make them longer if it helps.
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.
Is deleting the swiftmodule actually always an option? It could be a distributed binary module without library evolution enabled couldn't it?
I'm considerong moving the recommended solutions to separate notes
This would be good IMO. I think it'd be better if the error should be a short summary, with the rest in notes.
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.
Is deleting the swiftmodule actually always an option? It could be a distributed binary module without library evolution enabled couldn't it?
Yeah, we should only suggest it when the module is resilient I suppose.
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 reworked those diagnostics to make them simpler and shorter. They are now reported on the swiftmodule file making it more intuitive to see where is the broken reference.
I removed the hints to solutions and potential problems from this PR. I plan on replacing them with notes and further analysis of the context, i.e. hint that the swiftmodule is outdated only when resilient and distributed.
2c43e2b to
4fd7d8e
Compare
|
@swift-ci Please smoke test |
| // RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/B.swiftmodule -module-name B | ||
| // RUN: not %target-swift-frontend -emit-sil %t/Client.swiftmodule -module-name Client -I %t 2>&1 \ | ||
| // RUN: | %FileCheck --check-prefixes CHECK,CHECK-TYPE-CHANGED %s | ||
| // CHECK-TYPE-CHANGED: <unknown>:0: error: module reference to top-level 'MyType' broken by a context change, its type changed since building 'Client', it was in 'A' now found in 'A'. Ensure Swift language version match between modules or uninstall SDK roots. |
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.
Is it possible to show the type info here? e.g. it was a struct but now a func in module 'A'
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 should be able to. Since it failed to deserialize we pretty much just know if it's a type or if it's a ValueDecl. In most cases it's a type so having that precision should help.
| (Identifier)) | ||
|
|
||
| ERROR(modularization_issue_moved,Fatal, | ||
| "module reference to top-level %0 broken by a context change, " |
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.
would read better with ; instead of , after 'context change'
| "current compilation does not have -experimental-hermetic-seal-at-link", | ||
| (Identifier)) | ||
|
|
||
| ERROR(modularization_issue_moved,Fatal, |
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.
modularization_issue_decl_moved might be more clear
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.
Added decl_ to all of them and the related diagnostics.
| DiagnosticBehavior limit = DiagnosticBehavior::Fatal) const; | ||
|
|
||
| void log(raw_ostream &OS) const override { | ||
| OS << "modularization issue on '" << name << "', reference from '"; |
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.
referenced from
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 left this as is because it reads reference from X not resolvable. In theory nobody should see this error as it is replaced by the proper diagnostic.
| (DeclName, Identifier, StringRef, Identifier)) | ||
| ERROR(modularization_issue_not_found,Fatal, | ||
| "module reference to top-level %0 broken by a context change, " | ||
| "module '%2' references %0 in %1, but it cannot be found in this build. " |
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.
%0 cannot be found in this build
|
|
||
| public: | ||
| enum class Kind { | ||
| Moved, |
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.
DeclMoved
| enum class Kind { | ||
| Moved, | ||
| TypeChanged, | ||
| NotFound |
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.
DeclNotFound
| "Audit related headers or uninstall SDK roots.", | ||
| (DeclName, Identifier, StringRef, Identifier)) | ||
|
|
||
| NOTE(modularization_issue_effect_extension_error,none, |
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.
what does 'effect' here mean?
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.
Replaced it with side_effect, hopefully it's clearer.
|
|
||
| class TypeError : public llvm::ErrorInfo<TypeError, DeclDeserializationError> { | ||
| // Service class for errors with an underlying cause. | ||
| class CausedError { |
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.
ErrorCause might be clearer to read
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.
Changed it to ErrorWithUnderlyingReason.
Intro a new class ErrorWithUnderlyingReason to refactor duplicated logic from three classes. It can be used as super class to any error with an underlying error.
4fd7d8e to
f62af93
Compare
f62af93 to
7f8bf0b
Compare
lib/AST/DiagnosticEngine.cpp
Outdated
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 think it would be good to avoid this:
- It's a private header in
Serialization. If we wanted to use it we really should move it out into the top levelinclude. - This introduces a circular dependency -
Serializationdepends onClangImporterdepends onAST. AndASTdepends onSerializationnow.
Could we just add the new diagnose in Serialization and have it do the ModuleFile -> SourceLoc mapping instead? That would also avoid all the {} -> SourceLoc() changes.
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.
Good point, I'm looking into it.
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 added a ModuleFile::getSourceLoc() instead, it cleans up the PR nicely.
lib/AST/DiagnosticEngine.cpp
Outdated
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.
Any particular reason to have the filename in the fake buffer? For debugging? Doesn't really hurt I suppose.
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.
Replaced it with an empty string.
Generate a fake empty buffer to return a SourceLoc pointing to the beginning of a swiftmodule file.
The Swift compiler expects the context to remain stable between when a module is built and loaded by a client. Usually the build system would rebuild a module if a dependency changes, or the compiler would rebuilt the module from a swiftinterface on a context change. However, such changes are not always detected and in that case the compiler may crash on an inconsistency in the context. We often see this when a clang module is poorly modularized, the headers are modified in the SDK, or some clang define change its API. These are project issues that used to make the compiler crash, it provided a poor experience and doesn't encourage the developer to fix them by themselves. Instead, let's keep track of modularization issues encountered during deserialization and report them as proper errors when they trigger a fatal failure preventing compilation.
7f8bf0b to
144d7eb
Compare
|
@swift-ci Please smoke test |
| (bool, DeclName, Identifier, Identifier)) | ||
| ERROR(modularization_issue_decl_type_changed,Fatal, | ||
| "reference to %select{top-level|type}0 %1 broken by a context change; " | ||
| "the details of %1 %select{from %2|}5 changed since building '%3'" |
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.
Reading this diagnostic in context in the test case made me feel like the details of was a bit vague. Would the declaration kind of make sense? It would be especially helpful if we could print what kind of declaration we expected it to be and what it actually is but that seems like a stretch goal for another time.
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.
That's an improvement, I'll apply it in my next PR.
I took another look and we could print more details here. That diagnostics actually points out if there were candidate decls but all of them were filtered out. There's about a dozen reasons why a decl could be filtered out, most of them are simply to differentiate decls sharing a same name. However, we could modify that logic to collect the specific reasons that are triggered and show them in the diagnostics. I've seen that issue very rarely, about once a year, but when it does hit having more details could be critical. If time permits I'll come back to this diagnostic.
The Swift compiler expects the context to remain stable between when a module is built and loaded by a client. Usually the build system would rebuild a module if a dependency changes, or the compiler would rebuilt the module from a swiftinterface on a context change. However, such changes are not always detected and in that case the compiler may crash on an inconsistency in the context. We often see this when a clang module is poorly modularized, the headers are modified in the SDK, or some clang define change its API.
These are project issues that used to make the compiler crash, it provided a poor experience and doesn't encourage the developer to fix them by themselves. Instead, let's keep track of modularization issues encountered during deserialization and report them as proper errors when and if they trigger a fatal failure.
Next I'll look to add a mode to remark on any modularization issue detected, not only the fatal ones. And a mode to workaround some modules issues and recover when a type moves between modules.