Skip to content

[Sema] Don't look through CoerceExprs in markDirectCallee #27085

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
Sep 26, 2019

Conversation

hamishknight
Copy link
Contributor

@hamishknight hamishknight commented Sep 8, 2019

Doing so prevented the coercion of a function with argument labels to a user-written function type, as the reference would keep its argument labels due to being marked as "directly applied", but this would then prevent its coercion as the written function type cannot contain argument labels:

func foo(x: Int) {}
(foo as (Int) -> ())(5)
// error: Cannot convert value of type '(Swift.Int) -> ()' to type '(Swift.Int) -> ()'
// in coercion

This PR changes the behaviour such that the referenced function is considered to be unapplied, meaning it has its argument labels stripped, therefore allowing the coercion.

I'm unsure if this change requires any Swift evolution discussion – it is technically source breaking, as it's currently possible to use a generic type alias to make the compiler infer a function type with argument labels:

typealias Magic<T> = T

func foo(x: Int) {}
(foo as Magic)(x: 5)

But I would hope nobody is actually writing code like this, and I can't think of any other compelling examples that would break because of this change.

Resolves SR-11429.

Doing so prevented the coercion of a function with argument labels to a
user-written function type, as the latter cannot contain argument
labels. This commit changes the behaviour such that the referenced
function is considered to be unapplied, meaning it has its argument
labels stripped, therefore allowing the coercion.

Resolves SR-11429.
@hamishknight
Copy link
Contributor Author

@swift-ci please test

@hamishknight
Copy link
Contributor Author

@swift-ci please test source compatibility

@swift-ci
Copy link
Contributor

swift-ci commented Sep 8, 2019

Build failed
Swift Test OS X Platform
Git Sha - 1cc1e58

@hamishknight
Copy link
Contributor Author

@swift-ci please test OS X platform

Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

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

This seems right to me but @xedin would be a better person to check with. I think someone from the core team should probably say this is okay but it does seem obviously correct. That comment about disambiguation does seem possible, though.

@hamishknight hamishknight requested a review from xedin September 9, 2019 18:42
@hamishknight
Copy link
Contributor Author

@jrose-apple I believe the comment about disambiguation was made back when function types could have argument labels, so you could do things like:

func foo(x: Int) {}
func foo(y: Int) {}

(foo as (x: Int) -> Void)(x: 5)
(foo as (y: Int) -> Void)(y: 5)

But AFAIK it's no longer possible to perform such disambiguation using an intermediate coercion.

@jrose-apple
Copy link
Contributor

jrose-apple commented Sep 9, 2019

I thought it's still potentially relevant for things like

func foo(x: Int8) {}
func foo(x: UInt8) {}
(foo as (Int8) -> Void)(5)

…except that that already doesn't work

<stdin>:3:2: error: ambiguous reference to member 'foo(x:)'
(foo as (Int8) -> Void)(5)
 ^~~
<stdin>:1:6: note: found this candidate
func foo(x: Int8) {}
     ^
<stdin>:2:6: note: found this candidate
func foo(x: UInt8) {}
     ^

and you'll note I left out the label

<stdin>:3:24: error: extraneous argument label 'x:' in call
(foo as (Int8) -> Void)(x: 5)
                       ^~~~

So maybe we're in the clear.

@hamishknight
Copy link
Contributor Author

@jrose-apple Yeah, because we mark (overloaded) decl ref foo as being directly applied, when attempting overloads we don't strip the argument labels from the type, meaning that we're then unable to perform a coercion to a function type without arg labels. This PR would allow (foo as (Int8) -> Void)(5) to compile.

@slavapestov
Copy link
Contributor

Function types with argument labels are definitely not meant to be part of the type system, and it's mostly a historical artifact that this can be represented at all.

@slavapestov
Copy link
Contributor

I wonder if rdar://36209605 is related:

struct S {
  init() {}

  static func create(x: Int) -> S {
    return S()
  }
}

let s1: S = S.create(x:)(12) // OK
let s2: S = .create(x:)(12) // error: missing argument label 'x:' in call

@hamishknight
Copy link
Contributor Author

hamishknight commented Sep 11, 2019

@slavapestov That's indeed a similar issue, though an UnresolvedMemberExpr doesn't have its FunctionRefKind determined by markDirectCallee, instead it's decided when generating constraints:

https://github.com/apple/swift/blob/c79214c3c6f7d2a9880c76b93cae1ec3659dbc8f/lib/Sema/CSGen.cpp#L1469-L1471

We ought to be treating it as FunctionRefKind::Compound if its decl name is compound, even if it has an argument expr. Though unfortunately this would also be source breaking for IUO returns, for example the following would no longer compile:

struct S {
  static func foo(_ x: Int) -> S! { S() }
}

let s: S = .foo(_:)(5)

Unless we change the rules such that IUO unwraps are allowed through compound references which are otherwise directly applied.

Copy link
Contributor

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

This does seem reasonable to me and I'm be fine breaking the case mentioned in the description to make the behavior consistent, but I think we still need a core team sign-off on that. @DougGregor could you please take a look as well?

@DougGregor
Copy link
Member

Sorry for the delay; I'll bring this up with the core team

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.

The Core Team discussed this today and has approved the change. Thanks for your patience!

@hamishknight
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 1cc1e58

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 1cc1e58

@hamishknight
Copy link
Contributor Author

@swift-ci please smoke test

@hamishknight hamishknight merged commit 8a91e98 into swiftlang:master Sep 26, 2019
@hamishknight hamishknight deleted the coercion-indirection branch September 26, 2019 17:16
@jrose-apple
Copy link
Contributor

Worth mentioning in the CHANGELOG, since it both fixes a source-level issue and breaks obscure code?

@hamishknight
Copy link
Contributor Author

@jrose-apple Sure thing! I'll try and open a PR tomorrow.

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.

6 participants