-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[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 |
"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. " | ||
"Audit related headers or uninstall SDK roots.", | ||
(DeclName, Identifier, StringRef, Identifier)) |
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.
@@ -872,6 +872,31 @@ ERROR(need_hermetic_seal_to_import_module,none, | |||
"current compilation does not have -experimental-hermetic-seal-at-link", | |||
(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'
@@ -872,6 +872,31 @@ ERROR(need_hermetic_seal_to_import_module,none, | |||
"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.
@@ -359,82 +429,111 @@ class OverrideError : public llvm::ErrorInfo<OverrideError, | |||
} | |||
}; | |||
|
|||
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
@@ -32,6 +32,7 @@ | |||
#include "swift/Config.h" | |||
#include "swift/Localization/LocalizationFormat.h" | |||
#include "swift/Parse/Lexer.h" // bad dependency | |||
#include "../Serialization/ModuleFile.h" |
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 -
Serialization
depends onClangImporter
depends onAST
. AndAST
depends onSerialization
now.
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
auto filename = file->getModuleFilename(); | ||
auto bufferID = SourceMgr.getIDForBufferIdentifier(filename); | ||
if (!bufferID) | ||
bufferID = SourceMgr.addMemBufferCopy(filename, filename); |
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.