-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Serialization] Display contextual notes on deserialization errors and misconfigurations #66227
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 |
// CHECK-WORKAROUND: ^ | ||
// CHECK-WORKAROUND: LibWithXRef.swiftmodule:1:1: note: attempting forced recovery enabled by -experimental-force-workaround-broken-modules | ||
// CHECK-WORKAROUND: <binary format> | ||
// CHECK-WORKAROUND: ^ |
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.
@bnbarham I inverted the order back as we discussed. With the warning now showing the synthesized code I think this is more informative.
auto bufferID = SourceMgr.addMemBufferCopy("<binary format>", filename); | ||
return SourceMgr.getLocForBufferStart(bufferID); |
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.
Won't this create a new buffer every time (same applies to all the other getSourceLoc's)? Having said that, see my comment below (re. whether we want location at all).
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.
Yes. It's far from optimal but I don't think it's critical now since these buffers should only be allocated on failures or when remarks are enabled. We could reuse the buffers but I also want to investigate having diagnostics pointing to a file without a buffer at all, as not showing the <binary format>
buffer content would be an improvement on its own.
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 we do get a more optimized version, we could use it for existing services like extractNearestSourceLoc
.
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.
See my other comment. If you really want to keep, fair enough. It just seems like unknown makes more sense in these cases to me.
Though allocating one for every remark seems pretty bad to me 😅
lib/AST/Module.cpp
Outdated
auto &SourceMgr = getASTContext().Diags.SourceMgr; | ||
for (auto F : getFiles()) { | ||
if (auto SF = dyn_cast<SourceFile>(F)) { | ||
auto bufferID = SF->getBufferID(); |
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.
When would this path actually be taken, wouldn't they all be LoadedFile
?
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 SourceFile
is a FileUnit
but not a LoadedFile
. Still, that branch may be dead code at this point as no diagnostics should point to the currently building module, unless something went really wrong in the dependency order which I wouldn't fully rule out.
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 was mostly referring to your latter point. But fair enough 👍
@@ -15,6 +15,45 @@ | |||
|
|||
/// Main error downgraded to a remark. | |||
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: remark: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'A_related' | |||
|
|||
/// Contextual notes about the modules involved. | |||
// CHECK-MOVED: A.swiftmodule:1:1: note: declaration was expected to be found in this module |
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 just having no location be better and then a note of eg. declaration was expected to be found in 'A.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.
We don't see it in the test but the full path is the main point of interest here. If we don't have the path on the left side we get the <unknown>
.
How about ../../A.swiftmodule:1:1: declaration was expected to be found in module 'A' at this path
?
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.
In order of preference:
<unknown>: declaration was expected to be found in module 'A' at '../../A.swiftmodule'
../../A.swiftmodule: declaration was expected to be found in module 'A'
../../A.swiftmodule:1:1 declaration was expected to be found in module 'A'
../../A.swiftmodule:1:1 declaration was expected to be found in module 'A' at this path
My reasoning here is mostly that given this is more of a list of notes, the location at the start makes it a little confusing. Where as if they were all "unknown" you'd just skip over the unknown and then read the note.
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 we go for the first alternative, that's much simpler to support as I could get rid of the new ModuleDecl::getSourceModuleLoc
logic:
<unknown>: declaration was expected to be found in module 'A' at '../../A.swiftmodule'
This next alternative is what I'm considering doing next in a future PR, just pointing to the path without a fake buffer content. I think this would be clean and reminiscent of clang's diagnostic style:
../../A.swiftmodule: declaration was expected to be found in module 'A'
The last one is close to the current behavior from this PR. I see this as a step in the right direction but if it's not actually better than the <unknown>
variant and we're worried about creating too many buffers, I am considering just skipping that step:
../../A.swiftmodule:1:1 declaration was expected to be found in module 'A'
I'd be curious to hear more opinions about this.
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.
Yeah, I'll do that. It will allow me to deprioritize working on the diagnostic style and instead put more time on the content and making sure it's actually raised when we want it to.
|
||
/// More notes depending on the context | ||
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: note: this module was built with a Swift language version set to 5 | ||
// CHECK-MOVED: while the current invocation uses 4 |
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'm surprised this works. Shouldn't this need a CHECK-MOVED-SAME
?
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.
It worked but -SAME
will be more clear on the intent, I'll update it.
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: note: this module was built with a Swift language version set to 5 | ||
// CHECK-MOVED: while the current invocation uses 4 | ||
|
||
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: note: the module 'LibWithXRef' enabled library-evolution; this file may need to be deleted if the SDK was modified |
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.
Are these all notes of the previous remark?
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.
Yes they are. This is the worst case scenario, there won't be that many notes in most cases.
Preserve more information about the context of modularization errors by replacing the module names with pointers to ModuleDecl and ModuleFile objects.
This service provides a path to the loaded swiftmodule file and synthesizes pseudo-Swift code representing the reference.
@swift-ci Please smoke test |
Updated the text of a few diagnostics and removed the SourceLoc from the notes. Hopefully this makes the messages easier to read and understand. We may want to bring back the version with SourceLoc if we notice that users are confused as to where the actual problem is located. Here's how it looks now for the case triggering the most notes in the test for remarks:
|
Actually we do want the SourceLoc for the @swift-ci Please smoke test |
This PR makes diagnostics on deserialization errors caused by project configuration more helpful by providing contextual information on the issue: - Path to the modules involved (up to 4 modules): the loaded swiftmodule with the broken outgoing reference, the path to the module where the decl was expected, the path to the underlying clang module, and the path to the module where the decl was found. This information should prevent us from having to ask for a different log with `-Rmodule-loading`. - Hint to delete the swiftmodule files when the module is library-evolution enabled. - Hint that clang settings affect clang modules involved in this scenario. - Pointing out when a decl moved between two modules with a similar name (where one name is a prefix of the other). This is a common issue when headers are shared between a clang framework's public and private modules. - Pointing out layering issues when an SDK module imports a local module. - Pointing out Swift language version mismatch which may lead to the compiler viewing the same clang decls differently when they are modified by APINotes files.
@swift-ci Please smoke test |
Would it make sense for
to be the first note? |
These 3 notes would rarely show at the same time but 2 are likely to be shown together, as the first one would be very common. They could be more specific to avoid the general I like keeping the current error first as it's the problem to fix, whereas |
In most cases the root cause and the side-effects would still be in the same module, this makes my argument about the order less relevant. I'll give a try to move the side effect before the root cause and see the result. |
@swift-ci Please smoke test |
@bnbarham The last commit inverts the order of the diagnostics, making the side-effect the main anchor of the errors/warnings/remarks. It looks like so:
|
Oh no 😅. To be clear - I meant first note, ie. I was expecting:
|
Ah. I'll drop the last commit then. I think it could be good but it doesn't show as well currently, it would need improvements. I wouldn't move up that note because I think the first few notes (about the involved files) are important as direct additional information to the remark. They even duplicate some of the information so we could simplify the remark using them. And mixing the side effect with the details on the root cause may quickly become confusing. We may want to change it from a note to a remark/warning/error to make it stand out more if needed. |
@swift-ci Please smoke test |
Since #65713 the Swift compiler raises a proper error instead of crashing when hitting a deserialization issue likely caused by broken modularization or project settings. This PR makes these diagnostics more helpful by providing contextual information:
-Rmodule-loading
.These notes are also display with
-Rmodule-recovery
(#66139) and when-experimental-force-workaround-broken-modules
triggers and shows a warning (#66186).There is room to improve those diagnostics further. We're considering bubbling up decls marked invalid instead of passing up errors in deserialization, this would allow for more complete synthesized decl when pointing to the final effect of the failure. We may also need to prioritize diagnosing these errors at a few sites where the compiler would still crash. We could also added services to the diagnostics engine to support one-line diagnostics pointing to binary files, without showing source code or the
:1:1
.