Skip to content

Sema: Explicitly allow Optional-to-IUO when converting functions. #7153

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 1 commit into from
Jan 31, 2017

Conversation

jrose-apple
Copy link
Contributor

We limit Optional-to-IUO as an implicit conversion to avoid making common expressions ambiguous. However, this runs into trouble with function-to-function conversions, which always look for a "Subtype" relationship for their inputs and outputs. This is a conservative way for Sema to avoid emitting conversions that SILGen cannot handle.

The problem case here is converting a closure with type (Any!) -> Void to a value of type (Any?) -> Void:

let f: ((Any?) -> Void) = { (arg: Any!) in }

This is clearly a safe conversion, since Any! and Any? have the same representation at run time, but historically we've disallowed it because of the above rules. However, in Swift 3.0 this particular case was permitted, with the type checker deciding that the Any? argument to f could first itself be put into an Any, then that value could go through a value-to-optional conversion to make Any!. Fortunately the emitted code didn't follow this incorrect conversion path.

This patch doesn't even try to emulate the old behavior. Instead, it just weakens the restriction on Optional-to-IUO via a new type matching flag that only applies within the context of matching function types.

We can consider opening up function conversions in Swift 4 to anything that supports conversion—not just subtyping—now that SILGen knows how to automatically reabstract most such things. But whether we do or not, Optional/IUO is a special case.

SR-3758 / rdar://problem/30235814

@jrose-apple
Copy link
Contributor Author

@swift-ci Please 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.

Yeah, this looks okay as a narrow fix. Be warned that the cherry-pick to the 3.1 branch will be a little annoying, because enumerateOptionalConversionRestrictions is a refactor on master that didn't get cherry-picked.

Copy link
Contributor

@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.

LGTM. Glad you found a narrow fix here to restore the old behavior.

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test OS X Platform
Git Commit - 0d01210b85ff8806a46be1af35b05ccd4d252bbb
Test requested by - @jrose-apple

@@ -1232,7 +1233,7 @@ static bool isPotentiallyMoreOptionalThan(Type objType1,
/// Enumerate all of the applicable optional conversion restrictions
static void enumerateOptionalConversionRestrictions(
Type type1, Type type2,
ConstraintKind kind,
ConstraintKind kind, bool isFunctionInputOrResult,
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of adding another flag would it make more sense if you look at the locator instead?

@slavapestov
Copy link
Contributor

Are you going to do the proper fix too? ;)

BTW I think SILGen only supports Subtype conversions for function parameters and returns right now. This includes a fair number of conversions, like existential erasure, class upcasts, injection into optionals, and some random ones like metatype to object conversions. I think Conversion does some additional things that SILGen does not support.

@jrose-apple
Copy link
Contributor Author

I don't think there is a proper fix that maintains Swift 3 compatibility. If you look at the JIRA you can see a record of some of the other things I tried. Everything else has much wider ripple effects.

Looking at the locator's not a bad idea. The query's just "are we anywhere in ConstraintLocator::FunctionArgument or ConstraintLocator::FunctionResult". I'll try that.

@slavapestov
Copy link
Contributor

Look at how locators have a summary flags field. I think this gives you what you want.

@jrose-apple
Copy link
Contributor Author

Not sure what's up with the test failure. It has nothing to do with this patch.

We limit Optional-to-IUO as an implicit conversion to avoid making
common expressions ambiguous. However, this runs into trouble with
function-to-function conversions, which always look for a "Subtype"
relationship for their inputs and outputs. This is a conservative way
for Sema to avoid emitting conversions that SILGen cannot handle.

The problem case here is converting a closure with type '(Any!) ->
Void' to a value of type '(Any?) -> Void':

    let f: ((Any?) -> Void) = { (arg: Any!) in }

This is clearly a safe conversion, since 'Any!' and 'Any?' have the
same representation at run time, but historically we've disallowed it
because of the above rules. However, in Swift 3.0 this particular case
was permitted, with the type checker deciding that the 'Any?' argument
to 'f' could first itself be put into an 'Any', then /that/ value
could go through a value-to-optional conversion to make 'Any!'.
Fortunately the emitted code didn't follow this incorrect conversion
path.

This patch doesn't even try to emulate the old behavior. Instead, it
just weakens the restriction on Optional-to-IUO via a new type
matching flag that only applies within the context of matching
function types.

We can consider opening up function conversions in Swift 4 to anything
that supports conversion---not just subtyping---now that SILGen knows
how to automatically reabstract most such things. But whether we do or
not, Optional/IUO is a special case.

https://bugs.swift.org/browse/SR-3758
@jrose-apple
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test OS X Platform
Git Commit - 0d01210b85ff8806a46be1af35b05ccd4d252bbb
Test requested by - @jrose-apple

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test Linux Platform
Git Commit - 0d01210b85ff8806a46be1af35b05ccd4d252bbb
Test requested by - @jrose-apple

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test OS X Platform
Git Commit - 9d90d7e
Test requested by - @jrose-apple

@jrose-apple
Copy link
Contributor Author

@swift-ci Please test macOS

@jrose-apple jrose-apple merged commit eed34d1 into swiftlang:master Jan 31, 2017
@jrose-apple jrose-apple deleted the Optional-to-IUO branch January 31, 2017 21:42
jrose-apple added a commit to jrose-apple/swift that referenced this pull request Jan 31, 2017
…iftlang#7153)

We limit Optional-to-IUO as an implicit conversion to avoid making
common expressions ambiguous. However, this runs into trouble with
function-to-function conversions, which always look for a "Subtype"
relationship for their inputs and outputs. This is a conservative way
for Sema to avoid emitting conversions that SILGen cannot handle.

The problem case here is converting a closure with type '(Any!) ->
Void' to a value of type '(Any?) -> Void':

    let f: ((Any?) -> Void) = { (arg: Any!) in }

This is clearly a safe conversion, since 'Any!' and 'Any?' have the
same representation at run time, but historically we've disallowed it
because of the above rules. However, in Swift 3.0 this particular case
was permitted, with the type checker deciding that the 'Any?' argument
to 'f' could first itself be put into an 'Any', then /that/ value
could go through a value-to-optional conversion to make 'Any!'.
Fortunately the emitted code didn't follow this incorrect conversion
path.

This patch doesn't even try to emulate the old behavior. Instead, it
just weakens the restriction on Optional-to-IUO via a new type
matching flag that only applies within the context of matching
function types.

We can consider opening up function conversions in Swift 4 to anything
that supports conversion---not just subtyping---now that SILGen knows
how to automatically reabstract most such things. But whether we do or
not, Optional/IUO is a special case.

https://bugs.swift.org/browse/SR-3758
tkremenek pushed a commit that referenced this pull request Jan 31, 2017
) (#7168)

We limit Optional-to-IUO as an implicit conversion to avoid making
common expressions ambiguous. However, this runs into trouble with
function-to-function conversions, which always look for a "Subtype"
relationship for their inputs and outputs. This is a conservative way
for Sema to avoid emitting conversions that SILGen cannot handle.

The problem case here is converting a closure with type '(Any!) ->
Void' to a value of type '(Any?) -> Void':

    let f: ((Any?) -> Void) = { (arg: Any!) in }

This is clearly a safe conversion, since 'Any!' and 'Any?' have the
same representation at run time, but historically we've disallowed it
because of the above rules. However, in Swift 3.0 this particular case
was permitted, with the type checker deciding that the 'Any?' argument
to 'f' could first itself be put into an 'Any', then /that/ value
could go through a value-to-optional conversion to make 'Any!'.
Fortunately the emitted code didn't follow this incorrect conversion
path.

This patch doesn't even try to emulate the old behavior. Instead, it
just weakens the restriction on Optional-to-IUO via a new type
matching flag that only applies within the context of matching
function types.

We can consider opening up function conversions in Swift 4 to anything
that supports conversion---not just subtyping---now that SILGen knows
how to automatically reabstract most such things. But whether we do or
not, Optional/IUO is a special case.

https://bugs.swift.org/browse/SR-3758
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.

5 participants