Skip to content
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

Flatten nested optionals resulting from try? and optional sub-expressions #16942

Merged
merged 13 commits into from Nov 16, 2018

Conversation

bjhomer
Copy link
Contributor

@bjhomer bjhomer commented Jun 1, 2018

This PR implements the flattening of a double-optional type produced from code like let x = try? optionalThing(). The pitch thread can be found here: https://forums.swift.org/t/make-try-optional-chain-flattening-work-together/7415/55

@jrose-apple jrose-apple added the swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review label Jun 13, 2018
@bjhomer bjhomer force-pushed the bjhomer/optional-try-flattening branch 2 times, most recently from 3075793 to 2fcf429 Compare October 10, 2018 19:05
@bjhomer
Copy link
Contributor Author

bjhomer commented Oct 10, 2018

@brentdax Rebased on master, tests are passing locally. Could you run the compatibility suite tests?

@beccadax
Copy link
Contributor

@swift-ci please test source compatibility

Copy link
Member

@rudkx rudkx left a comment

Choose a reason for hiding this comment

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

This change would benefit from additional test cases.

For example, what happens with something like:

func foo() throws -> Int? { return 1 }
let x = (try? foo())!!

With the new semantics I would expect this to fail type checking, hopefully with a good diagnostic.

It's possible we already have some interesting test cases along these lines, but they are in files not currently compiled with -swift-version 5. Can you take a look and add some more comprehensive tests to a file that is compiled with -swift-version 5?

@@ -2848,7 +2848,29 @@ namespace {
}

Expr *visitOptionalTryExpr(OptionalTryExpr *expr) {
return simplifyExprType(expr);
if (cs.getTypeChecker().getLangOpts().isSwiftVersionAtLeast(5) == false) {
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Can you change this to if (! ...) rather than if ( ... == false) as the former is used extensively throughout the codebase and is what people expect?

Not nitpick: Can you add a comment above the if explaining what the behavioral difference is from Swift 4 to Swift 5+?

@rudkx rudkx requested a review from xedin October 26, 2018 00:01
@bjhomer
Copy link
Contributor Author

bjhomer commented Oct 29, 2018

Yep, I'll get on this.

Copy link
Member

@xedin xedin left a comment

Choose a reason for hiding this comment

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

I agree with @rudkx, it would be great to add more tests for this, otherwise changes to Sema LGTM!

@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 2, 2018

I have added a bunch more tests. A couple of them are currently failing; it appears I need to make some changes for cases where we used to suggest changing try? to try!.

@natecook1000 natecook1000 added swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process and removed swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review labels Nov 2, 2018
If the sub-expression of the 'try?' is optional, the result will be the same level of optional-ness.
If the sub-expression is non-optional, the result is optional.

Thus, the following lines all end up with the same type of 'Int?'
 - let x = try? 3 as Int
 - let x = try? 3 as? Int
 - let x = try? 3 as Int?
This migrates anything using the following pattern:

```swift
if let optX = try? someOptional(),
    let x = optX,
    ...
```

to this:

```swift
if let x = try? someOptional(),
    ...
```
Some of these are currently failing. For example:

```swift
// expected-error {{cannot convert value of type 'Int??' to specified type 'Int'}}
let _: Int = try? producer.produceDoubleOptionalInt()
```

This actually produces this warning:

```
value of optional type 'Int?' not unwrapped; did you mean to use 'try!' or chain with '?'?
```

Note that the value in question is not an `Int?`, so a suggestion of `try!` here is inappropriate.
(And, due to the change in semantics of 'try?', wouldn't change anything anyway.)
Since 'try?' no longer unconditionally adds a layer of optional, converting it
to 'try!' will no longer unconditionally remove a layer of optional. So let's not
suggest it. This leads to better diagnostics anyway.
@bjhomer bjhomer force-pushed the bjhomer/optional-try-flattening branch from 3d302a5 to 1789d44 Compare November 7, 2018 08:03
@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 7, 2018

@xedin, @rudkx Tests have been added. I discovered that a fix-it would suggest changing try? into try! in cases where that would no longer fix anything, so that has also been addressed. I think we're ready for further review.

@rudkx
Copy link
Member

rudkx commented Nov 7, 2018

@swift-ci Please smoke test

@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift
// RUN: %target-typecheck-verify-swift -swift-version 5
Copy link
Member

Choose a reason for hiding this comment

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

Can you split this into two tests, one for ambient-version mode (with no changes to the existing test) and one for -swift-version 5?

We still want to test the older behavior as long as it is supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I can do that.

@rudkx
Copy link
Member

rudkx commented Nov 7, 2018

@nkcsgexi I cannot seem to currently add you as a reviewer. Can you take a look at the migrator changes?

@rudkx rudkx requested a review from jckarter November 7, 2018 22:48
@nkcsgexi nkcsgexi self-requested a review November 7, 2018 22:55
@nkcsgexi
Copy link
Member

nkcsgexi commented Nov 7, 2018

hmm, interesting. I've added myself as a reviewer and will take a look.

Copy link
Member

@rudkx rudkx left a comment

Choose a reason for hiding this comment

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

I looked at the AST & Sema changes as well as the tests. LGTM. Once you split out the new tests into a separate file for -swift-version 5 I think that portion is good to go.

@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 8, 2018

Regarding the migration, I have a test file that I've been using locally. I would be happy to add it as an automated test, if I could figure out how. Is there some example of how to run a test like that in the repository right now?

@rudkx
Copy link
Member

rudkx commented Nov 9, 2018

@nkcsgexi Can you answer the question re: migration testing?

@rudkx
Copy link
Member

rudkx commented Nov 9, 2018

@swift-ci Please smoke test

@nkcsgexi
Copy link
Member

nkcsgexi commented Nov 9, 2018

@bjhomer you can follow some examples in test/migrator. Taking remove_override.swift as an example, it's both test driver and the code example before migration. After running the migrator, the result should be identical to remove_override.swift.expected.

@bjhomer bjhomer force-pushed the bjhomer/optional-try-flattening branch from c69cbde to c371c06 Compare November 12, 2018 16:49
@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 12, 2018

@nkcsgexi: Thanks for that example. I'm bulking up these tests, and it's pointing out a few things I need to improve.

We were trying to get fancy rewriting cases where multiple 'if let' clauses were
used to unwrap a double optional, but in testing that turned out to be too
unreliable. Now we simply detect any 'try?' expressions whose behavior will change,
and add an explicit 'as OldType' to the expression, in order to preserve the original
behavior.
@bjhomer bjhomer force-pushed the bjhomer/optional-try-flattening branch from fbfd976 to f166b0d Compare November 14, 2018 08:10
@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 14, 2018

The migration tests are ready to go. I've simplified the approach a bit after discovering some problems with the earlier one.

It makes it clearer what has changed.
@tkremenek
Copy link
Member

This would be good to land before Swift 5 branches one last time from master on November 16.

@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 15, 2018

Agreed. I think everything is ready to go, but if there's any further feedback, I'll be responsive.

@natecook1000
Copy link
Member

@swift-ci Please smoke test

@natecook1000
Copy link
Member

@nkcsgexi @rudkx Okay to merge?

@nkcsgexi
Copy link
Member

The migrator part LGTM to merge!

@rudkx
Copy link
Member

rudkx commented Nov 16, 2018

Yes, merging.

@rudkx rudkx merged commit 018498f into apple:master Nov 16, 2018
@rudkx
Copy link
Member

rudkx commented Nov 16, 2018

@bjhomer It looks like this broke the foundation build: https://ci.swift.org/job/swift-PR-Linux-smoke-test/11232/consoleFull#-1189064102373bf607-d277-47c6-812e-27acc852624c

/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test/branch-master/swift-corelibs-foundation/TestFoundation/TestURL.swift:38:38: error: cannot force unwrap value of non-optional type '[String : Any]'
13:46:22     guard let parsingTests = testRoot![kURLTestParsingTestsKey] as? [Any] else {

@rudkx
Copy link
Member

rudkx commented Nov 16, 2018

Okay, I don't see how this passed PR testing yesterday but would now fail today. I might be missing something that changed in foundation. Reverting for now, though.

@bjhomer
Copy link
Contributor Author

bjhomer commented Nov 16, 2018

Fixed in apple/swift-corelibs-foundation#1777

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants