Skip to content

[Sema] More diagnostic improvement when an optional value is not unwrapped. #18094

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

Merged
merged 2 commits into from
Jul 27, 2018

Conversation

gregomni
Copy link
Contributor

This builds on Doug's pull #17921 by bringing in a simpler non-recursing subset of my pull #15321.

The changes here create a disjunction for the result of a FixKind::UnwrapOptionalBase since the result can be either the original member type (for ! or ? on members that return optionals) or an optional of the member type (for ? on members returning non-optional). Then which of the two is chosen determines the fixit notes produced.

To be cautious about what the user intends (and because it only looks at a single step instead of a potential multi-step chain), this continues to always offer optional chaining as a possible fixit, but now it won't suggest force-unwrapping where that doesn't make sense.

@gregomni gregomni requested a review from DougGregor July 19, 2018 23:38
// (for '!' or optional members with '?'), or the original member type with
// one extra level of optionality ('?' with non-optional members).
auto innerTV =
createTypeVariable(locator, TVO_CanBindToLValue | TVO_CanBindToInOut);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be able to bind to inout.

@@ -3478,6 +3478,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
// Resolve the base type, if we can. If we can't resolve the base type,
// then we can't solve this constraint.
// FIXME: simplifyType() call here could be getFixedTypeRecursive?
Copy link
Contributor

@CodaFi CodaFi Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this comment (I know it's not yours)? The answer is no.

// implicit optional, don't try to fix it. The IUO will be forced instead.
auto resolvedOverload = getResolvedOverloadSets();
while (resolvedOverload) {
if (resolvedOverload->BoundType->getCanonicalType() ==
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolvedOverload->BoundType->isEqual(originalBaseTy)

@CodaFi
Copy link
Contributor

CodaFi commented Jul 20, 2018

Overall, the approach makes sense. I'm still unhappy with this whole arrangement - especially because CSSimplify is where the typing rules live and we should pull recovery modes like this out into CSDiag.

I think you could shift this approach into CSDiag if you set up richer locators but I haven't looked into this deep enough to say more than that. Doug can say more here.

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the direction of this, but have a few comments that I think are straightforward to address.

// First type already appropriately set.
auto result = matchTypes(type1, type2, matchKind, subflags, locator);
if (result == SolutionKind::Solved)
if (recordFix(fix, locator))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that you recordFix() first, then directly return the result of the call to matchTypes. That will handle the "unresolved" case appropriately, rather than losing the fix.

// implicit optional, don't try to fix it. The IUO will be forced instead.
auto resolvedOverload = getResolvedOverloadSets();
while (resolvedOverload) {
if (resolvedOverload->BoundType->isEqual(originalBaseTy)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that checking BoundType for equality with originalBaseTy will suffice; it could match for unrelated cases. Could you check the locators here instead, like you're doing in CSApply.cpp?


// Assumes that these fixes are always created in pairs, with the first
// created non-optional and the second with an added optional.
return (Data % 2) == 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's very clever. I feel like the name of this method is misleading, though. Perhaps isUnwrapOptionalBaseByOptionalChaining()? I know it's long, but that's what we're looking for---the chaining case.

@gregomni
Copy link
Contributor Author

Thanks @CodaFi, @DougGregor! Made the requested changes.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thank you!

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test and merge.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@DougGregor
Copy link
Member

Would you mind rebasing before we kick off a testing cycle?

@gregomni
Copy link
Contributor Author

Yep, my fault for messing around with github's web interface to mis-perform a trivial merge :)

Rebased and squashed.

member access if optional chaining is sure to be valid.
@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@DougGregor
Copy link
Member

Thanks!

@gregomni gregomni merged commit 3b59384 into swiftlang:master Jul 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants