-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Make sure protocol witness errors don't leave the conformance context #16489
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
Make sure protocol witness errors don't leave the conformance context #16489
Conversation
Let's be consistent!
@swift-ci Please test |
@swift-ci Please test source compatibility |
Still needs new tests for the new behavior in addition to the updates for the existing tests, but I want to see if anything breaks, or if reviewers have objections. |
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.
@jrose-apple What sort of review are you looking for here? Since I'm not familiar with the type-checker, I can't quickly offer thoughts on correctness. If you let me know what would be most helpful, I will do it. A very quick glance at the diffs did not result in anything jumping out at me.
For you and Graydon it was more a CC than a review request, I guess. "Here's the part I've been working on, here's the general approach I took". It's Huon and/or Doug, who've been working with conformance checking more, that I really want to look over this. |
f6a1586
to
ba5bca6
Compare
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.
LGTM
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.
Minor questions other than the ctor->isImplicit
which is a just slightly-more-than-minor question.
lib/Sema/TypeCheckProtocol.cpp
Outdated
// If the main diagnostic is emitted on the conformance, we want to | ||
// attach the fix-it to the note that shows where the initializer is | ||
// defined. | ||
fixItDiag.reset(); |
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.
The destructor-based emission of diagnostics makes this if
kinda... magical.
Referencing the instances below, it seems this could almost be factored out, maybe?
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 there's enough commonality to factor it out, but I just realized that fixItDiag->flush()
will work as well, since InFlightDiagnostic tracks whether the diagnostic has been flushed yet. I'll do that instead.
lib/Sema/TypeCheckProtocol.cpp
Outdated
diag::witness_initializer_failability, | ||
ctor->getFullName(), | ||
witnessCtor->getFailability() | ||
== OTK_ImplicitlyUnwrappedOptional) | ||
.highlight(witnessCtor->getFailabilityLoc()); | ||
if (diagLoc != witness->getLoc()) { |
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 seems every second diagnostic touched here gets this if
block almost verbatim. We could consider factoring it out somehow?
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 guess that makes sense for all the decl_declared_here
ones, at least. I tried to think of how to fold it into getLocForDiagnosingWitness
with a callback and couldn't, then tried to think of how to generalize it over any possible follow-up note and couldn't. But more of them ended up just being "declared here" than I originally thought.
witness->getFullName(), | ||
proto->getFullName()); | ||
if (diagLoc == witness->getLoc()) { | ||
addOptionalityFixIts(adjustments, ctx, witness, diag); |
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's the thinking behind these only applying in this case, rather than the reset
/emplace
thing you've done for other similar versions to get fixits on 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.
I mostly only did that where there was a dedicated note, rather than just a "declared here". There are two reasons for that: "declared here" doesn't tell you what the fix-it is doing, and if the declaration is written somewhere else, it's less likely that its sole purpose is to satisfy this protocol. The one exception was the required
initializer fix-it, because people complain that they never remember what they have to write for initializers but I couldn't think of a good custom note for that case.
Happy to accept feedback here.
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.
Sounds like reasonable logic.
ctor->getFullName())); | ||
} | ||
if (!inExtension) { | ||
fixItDiag->fixItInsert(ctor->getAttributeInsertionLoc(true), |
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 this has changed behaviour: it will get attached to the witness_initializer_not_require
diagnostic if ctor->isImplicit()
?
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's subtle, but for an implicit constructor getAttributeInsertionLoc
will (now) return an invalid location, which drops the fix-it. I found this more natural than trying to untangle which conditions are needed and which aren't.
ba5bca6
to
2be4616
Compare
@swift-ci Please smoke test |
@swift-ci Please smoke test Linux |
lib/Sema/TypeCheckProtocol.cpp
Outdated
type, genericParam, !genericParam.isNull(), | ||
assocType->getFullName(), Proto->getFullName()); | ||
if (typeDecl && diagLoc != typeDecl->getLoc()) | ||
ctx.Diags.diagnose(typeDecl, diag::type_declared_here); |
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.
emitDeclaredHereIfNeeded(ctx.Diags, diagLoc, typeDecl)
?
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.
Huh. This one was "type declared here" instead of "'Foo' declared here", but I guess we have other type witness diagnostics where I went with the "'Foo' declared here" one.
witness->getFullName(), | ||
proto->getFullName()); | ||
if (diagLoc == witness->getLoc()) { | ||
addOptionalityFixIts(adjustments, ctx, witness, diag); |
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.
Sounds like reasonable logic.
That is, if there's a problem with a witness, and the witness comes from a different extension from the conformance (or the original type, when the conformance is on an extension), put the main diagnostic on the conformance, with a note on the witness. This involves some shuffling and rephrasing of existing diagnostics too. There's a few reasons for this change: - More context. It may not be obvious why a declaration in file A.swift needs to be marked 'public' if you can't see the conformance in B.swift. - Better locations for imported declarations. If you're checking a conformance in a source file but the witness came from an imported module, it's better to put the diagnostic on the part you have control over. (This is especially true in Xcode, which can't display diagnostics on imported declarations in the source editor.) - Plays better with batch mode. Without this change, you can have diagnostics being reported in file A.swift that are tied to a conformance declared in file B.swift. Of course the contents of A.swift also affect the diagnostic, but compiling A.swift on its own wouldn't produce the diagnostic, and so putting it there is problematic. The change does in some cases make for a worse user experience, though; if you just want to apply the changes and move on, the main diagnostic isn't in the "right place". It's the note that has the info and possible fix-it. It's also a slightly more complicated implementation.
2be4616
to
6bd7e5e
Compare
@swift-ci Please smoke test |
Make sure protocol witness errors don't leave the conformance context (cherry picked from commit 88169d6)
Make sure protocol witness errors don't leave the conformance context (cherry picked from commit 88169d6)
That is, if there's a problem with a witness, and the witness comes from a different extension from the conformance (or the original type, when the conformance is on an extension), put the main diagnostic on the conformance, with a note on the witness. This involves some shuffling and rephrasing of existing diagnostics too.
There's a few reasons for this change:
More context. It may not be obvious why a declaration in file A.swift needs to be marked 'public' if you can't see the conformance in B.swift.
Better locations for imported declarations. If you're checking a conformance in a source file but the witness came from an imported module, it's better to put the diagnostic on the part you have control over. (This is especially true in Xcode, which can't display diagnostics on imported declarations in the source editor.)
Plays better with batch mode. Without this change, you can have diagnostics being reported in file A.swift that are tied to a conformance declared in file B.swift. Of course the contents of A.swift also affect the diagnostic, but compiling A.swift on its own wouldn't produce the diagnostic, and so putting it there is problematic. (See [Batch Mode] Suppressing diagnostics for non-primaries - rdar://40032762 #16485.)
The change does in some cases make for a worse user experience, though; if you just want to apply the changes and move on, the main diagnostic isn't in the "right place". It's the note that has the info and possible fix-it. It's also a slightly more complicated implementation.