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

[SR-12261] Erratic type inference makes it seem like an error can be detected at compile time or runtime or not at all #54689

Open
jepers opened this issue Feb 24, 2020 · 2 comments

Comments

@jepers
Copy link

@jepers jepers commented Feb 24, 2020

Previous ID SR-12261
Radar rdar://problem/59739719
Original Reporter @jepers
Type Bug
Environment

Default toolchain of Xcode 11.3.1 (11C504)

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug, CompileTime, Runtime, TypeChecker
Assignee None
Priority Medium

md5: 9534538213f24118d8a1a13770a2c36f

Issue Description:

Forum post and thread with further details:
https://forums.swift.org/t/34044/6

Demonstration Program A (shows expected behavior):

struct G<T> { var value: T }
func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }

let a: G<(id: Int,  data: String)> = G(value: (123, "abc"))
let b: G<(id: Int, daata: String)> = foo(a) {
    return $0 // <-- Error here as expected (misspelled daata -> mismatched array types)
}

Demonstration Program B (shows unexpected behavior for Array):

func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }

let a: [(id: Int,  data: String)] = [(123, "abc")]
let b: [(id: Int, daata: String)] = foo(a) {
    // _ = () // <-- Uncomment this line to detect error on
    return $0 // <-- this line at compile- instead of run time.
}

Demonstration Program C (shows a different unexpected behavior for Optional):

func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }

let a: (id: Int,  data: String)? = (123, "abc")
let b: (id: Int, daata: String)? = foo(a) {
    // _ = () // <-- Uncomment for compile time error on the next line, but note that it will compile and run as long as this line is commented away.
    return $0 // <-- No runtime error here when it's `Optional<Tuple>` instead of `Array<Tuple>`.
}
print(b ?? "") // Prints `(id: 123, daata: "abc")`
@typesanitizer
Copy link

@typesanitizer typesanitizer commented Feb 24, 2020

@swift-ci create

@CodaFi
Copy link
Member

@CodaFi CodaFi commented Feb 29, 2020

Definitely a quirk of the closure inference rules. Let's take program B:

let b: [(id: Int, daata: String)] = foo(a) {
    return $0 // <-- this line at compile- instead of run time.
}

The single-expression closure causes the body to be treated as one big expression. That expression's type is bound to the function type (T) -> R. While the labels do not match, there's an applicable conversion: the argument labels don't matter for closures. So we insert a function conversion node and everything goes off without a hitch.

Now, for the multi-statement closure.

let b: [(id: Int, daata: String)] = foo(a) {
    // _ = () // <-- Uncomment this line to detect error on
    return $0 // <-- this line at compile- instead of run time.
}

We cannot treat the body as before, so we must instead make do with what we do have: T is contextually bound to `(id: Int, data: String)?` via the argument a, and the return type contextually bound via the user-provided type (id: Int, daata: String)?. As there is no valid conversion rule between tuples with different sets of labels, the return type constraint fails.

The takeaways: Despite being unintuitive, this behaves correctly. Now, does it behave intuitively? Absolutely not. The inference rules for multi-statement closures really ought to be revisited to make these cases consistent one way or the other.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants