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
Conversation
3075793
to
2fcf429
Compare
@brentdax Rebased on master, tests are passing locally. Could you run the compatibility suite tests? |
@swift-ci please test source compatibility |
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.
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
?
lib/Sema/CSApply.cpp
Outdated
@@ -2848,7 +2848,29 @@ namespace { | |||
} | |||
|
|||
Expr *visitOptionalTryExpr(OptionalTryExpr *expr) { | |||
return simplifyExprType(expr); | |||
if (cs.getTypeChecker().getLangOpts().isSwiftVersionAtLeast(5) == false) { |
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.
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+?
Yep, I'll get on 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.
I agree with @rudkx, it would be great to add more tests for this, otherwise changes to Sema LGTM!
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 |
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.
3d302a5
to
1789d44
Compare
@swift-ci Please smoke test |
test/Parse/try.swift
Outdated
@@ -1,4 +1,4 @@ | |||
// RUN: %target-typecheck-verify-swift | |||
// RUN: %target-typecheck-verify-swift -swift-version 5 |
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.
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.
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 can do that.
@nkcsgexi I cannot seem to currently add you as a reviewer. Can you take a look at the migrator changes? |
hmm, interesting. I've added myself as a reviewer and will take a look. |
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 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.
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? |
@nkcsgexi Can you answer the question re: migration testing? |
@swift-ci Please smoke test |
@bjhomer you can follow some examples in test/migrator. Taking |
c69cbde
to
c371c06
Compare
@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.
fbfd976
to
f166b0d
Compare
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.
This would be good to land before Swift 5 branches one last time from |
Agreed. I think everything is ready to go, but if there's any further feedback, I'll be responsive. |
@swift-ci Please smoke test |
The migrator part LGTM to merge! |
Yes, merging. |
@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
|
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. |
Fixed in apple/swift-corelibs-foundation#1777 |
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