-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Sema] Always look through optionals for unresolved member lookup #34715
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
constraintKind == ConstraintKind::UnresolvedValueMember) { | ||
if (auto objectType = instanceTy->getOptionalObjectType()) { | ||
// If we don't have a wrapped type yet, we can't look through the optional | ||
// type. | ||
if (objectType->getAs<TypeVariableType>()) { | ||
if (objectType->getAs<TypeVariableType>() && result.ViableCandidates.empty()) { |
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 would break source compatibility when score of something found on Wrapped
be better than score on Optional
itself...
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.
Since we already score Decl
better than DeclViaUnwrappedOptional
, the score on Wrapped
would have to outweigh the disadvantage from the OverloadChoiceKind
mismatch. Do you think such situations are likely to occur in practice?
Is there any way to unconditionally prefer the non-optional choices, in the case where we've found multiple solutions?
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.
Since we already score Decl better than DeclViaUnwrappedOptional
Solutions are first ranked on the score produced by the solver (https://github.com/apple/swift/blob/main/lib/Sema/CSRanking.cpp#L758) and then their declaration diff is compared if scores match. It's quite possible that choices could have different optionality of require other implicit conversion which would change the behavior 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.
@xedin Ah, thank you for pointing that out. The situation where we have identically-named static members on both Optional<Wrapped>
and Wrapped
where both properly type-check seems pretty unlikely to me, but if it's above the risk level for being considered source-compatible (as opposed to just a bug) then so be it.
Is there a way to add the Wrapped
overloads but not skip over them whenever any Optional
overloads successfully type check?
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.
One way to implement this without changing existing behavior would be to lift DeclViaUnwrappedOptional
into the solver score to make sure regular declarations are always preferred over the ones found on wrapped type.
@xedin I've taken your suggestion and lifted |
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! I think might be worth it to add a unit test for this behavior as it might be much easier to make sure the behavior is correct without having to rely on debug output formatting of the solver.
@xedin Yeah that makes sense! Is there a good example I could refer to about how to do that? I haven't written a test like that before. |
I added a couple of unit tests which use |
Alright, I'll give those a look—thanks! Will ping you when this PR is ready for another look. |
Thank you! |
@xedin Finally got a chance to sit down with this and realized that relying on the solver output is overkill. All we really need is the type-checked AST, so I've switched to just checking the output of |
@Jumhyn I'd still prefer if we had that as a unit test but I think it's okay to use |
@Jumhyn @xedin After those changes member lookup here https://github.com/apple/swift/blob/a9f5107b44c71929521ffbf3a91c5081554f272d/lib/Sema/CSSimplify.cpp#L7183 will return both candidates for Optional and Wrapped types? Just checking as I'm trying to change the CSApply warning to a fix and right now we have to do another lookup ... |
@LucianoPAlmeida Yes, the results from that line should include candidates from both |
Great thanks, I think that could simplify things in that case :) |
@LucianoPAlmeida Unfortunately, I probably won't have a chance for the next couple of days to write the unit test to get this mergeable, so you may have to wait for a bit for the changes here to be usable for your fix. |
No worries @Jumhyn :) |
@LucianoPAlmeida It intentionally only unwrap a single layer of optionality at the moment. |
Ah, thank you @xedin :) |
That would be okay since it's performed in diagnostic mode anyway. |
Humm, that may be a problem because since it is a warning we have to always record it ... but on the other hand the extra lookup will be done only in certain situations involving deep optionals. I'm finishing up the changes and pushing, so we can discuss it there :) |
ce979a0
to
ef52a0a
Compare
Thank you for your patience @xedin! Finally had a chance to sit down an write up the unit test(s). Let me know if this matches what you were picturing. cc @LucianoPAlmeida, this will hopefully be merge-ready soon! :) |
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.
Looks great! That’s exactly what I had in mind.
@xedin Updated per your comment! Would you mind kicking off the tests on this PR? |
@swift-ci please test |
@swift-ci please test source compatibility |
Build failed |
Build failed |
@LucianoPAlmeida Latest push addresses your comments and fixes the test failure on Windows (in theory🤞). From what I could tell the Linux and macOS test failures were not related to the code touched here... mind kicking off another round of tests? |
@swift-ci please test |
@swift-ci please test source compatibility |
Build failed |
@swift-ci please test MacOS Platform |
@swift-ci Please test OS X platform |
Windows failure looks like a flake. Rebased against |
@swift-ci please test |
@swift-ci please test source compatibility |
@xedin All checks passed! |
Previously, we only looked through optionals on
UnresolvedMemberExpr
s when there were no results fromOptional
itself. However, in cases whereOptional
has a member with the same name, this would prevent programs from compiling even if the member onOptional
was obviously invalid in the context of theUnresolvedMemberExpr
. Consider this example:In SwiftUI,
Optional
conditionally conforms toView
wheneverWrapped
conforms toView
. Additionally,View
has an extension which provides an instance method namedbackground(_:alignment:)
. This means that unresolved member lookup ofbackground
will find the method onOptional
, and won't continue to look inUIColor
for the user-definedbackground
member.This PR allows the above code to compile by unconditionally looking into
Wrapped
for unresolved member lookup, even if we already found results inOptional<Wrapped>
. Because we already prefer overloads found onOptional
to those found onWrapped
in CSRanking, situations where there are valid members on both should continue to choose the member onOptional
(though this definitely needs a source-compat run).Resolves SR-13815.