swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.66k stars 10.38k forks source link

Type inference for default expressions (SE-347) generic closure issue #63749

Open stephencelis opened 1 year ago

stephencelis commented 1 year ago

Description

There seem to be some edge cases around identity functions that cause problems for SE-0347.

Steps to reproduce

A simple example:

struct S<A, B> {
  func f<C, D>(
    a2c: (A) -> C = { $0 },  // 🛑
    b2d: (B) -> D = { $0 }   // 🛑
  ) -> S<C, D> {
    .init()
  }
}

🛑 Unable to infer type of a closure parameter '$0' in the current context

While it'd be nice for this to work, the limitation is somewhat understandable. Making things more explicit, gets the base method compiling:

struct S<A, B> {
  func f<C, D>(
    a2c: (A) -> C = { (a: A) -> A in a },  // ✅
    b2d: (B) -> D = { (b: B) -> B in b }   // ✅
  ) -> S<C, D> {
    .init()
  }
}

// or even flipping it:

struct S<A, B> {
  func f<C, D>(
    a2c: (A) -> C = { (c: C) -> C in c },  // ✅
    b2d: (B) -> D = { (d: D) -> D in d }   // ✅
  ) -> S<C, D> {
    .init()
  }
}

But actual use leads to ambiguity if generics are unknown:

let s1 = S<Int, Int>()

let s2 = s1.f(a2c: { $0 + 1 }, b2d: { $0 + 1 })  // ✅
let s3 = s1.f(a2c: { $0 + 1 })  // 🛑 Generic parameter 'D' could not be inferred
let s4 = s1.f(b2d: { $0 + 1 })  // 🛑 Generic parameter 'C' could not be inferred
let s5 = s1.f()  // 🛑 Generic parameter 'C' could not be inferred
                 // 🛑 Generic parameter 'D' could not be inferred

Expected behavior

I expect the above to compile.

Environment

stephencelis commented 1 year ago

@xedin Hopefully not too painful of a thing to support :smile:

AnthonyLatsis commented 1 year ago

Here is a somewhat simpler example:

struct Foo<A> {
  func f<B>(_: B.Type = A.self) {}
}

do {
  Foo<Int>().f()
}
xedin commented 1 year ago

@stephencelis I don't think we can support { $0 } because we don't actually inject expressions into call site but use value of expression type instead, in order to form that value we need to make sure that it could be used to infer types for parameter which means that parameter type of (A) -> C is opened into ($T_A) -> $T_C and there are no types in the body to substituted for that.

Same with @AnthonyLatsis's example - = A.self should be rejected by the compiler because the expression couldn't be used in expression context to infer B.Type.

AnthonyLatsis commented 1 year ago

but use value of expression type instead

@xedin I am not sure I follow. Can we not infer B by opening B.Type and the type of A.self and equating them?

xedin commented 1 year ago

@AnthonyLatsis types of default expressions are not opened because again the actual value expression is not used in expression context which means that A.Type in this case acts as a archetype of generic parameter A in regards to expression Foo<Int>().f() and that's invalid, the compiler should have rejected _: B.Type = A.self parameter declaration.

AnthonyLatsis commented 1 year ago

@stephencelis I can either close this as expected behavior or turn it into a feature request. Which do you prefer?

stephencelis commented 1 year ago

I think a feature request would be nice. It does feel like a lot of Swift's type checking is getting super nuanced these days, so capturing more naive expectations would probably be a good idea.

AnthonyLatsis commented 1 year ago

Ah, I forgot our examples exposed a bug where default arguments are not rejected. I will open a new issue for those then.