swiftlang / swift

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

Generic struct using type pack can't store closure using same type pack in property #68369

Closed martialln closed 9 months ago

martialln commented 1 year ago

Description The following code doesn't compile on the last toolchain

struct Foo<each T> {
    let foo: (repeat each T) -> Void

    init(
        fn: @escaping (repeat each T) -> Void
    ) {
        self.foo = fn
    }
}

Compiler respond the following message with Xcode 15.0 beta 8 (15A5229m) and swift-DEVELOPMENT-SNAPSHOT-2023-09-04-a toolchain

error: type of expression is ambiguous without a type annotation
        self.foo = fn
        ~~~~~~~~~^~~~

Steps to reproduce

Expected behavior

Output of the same command with -debug-constraints:

---Constraint solving at [Sources/main.swift:7:9 - line:7:20]---
  (overload set choice binding $T0 := @lvalue Foo<repeat each T>)
  (overload set choice binding $T1 := @lvalue ($T3) -> Void [each T := $T2])
  (overload set choice binding $T4 := (repeat each T) -> Void)

---Initial constraints for the given expression---
(assign_expr type='()' location=Sources/main.swift:7:18 range=[Sources/main.swift:7:9 - line:7:20]
  (unresolved_dot_expr type='@lvalue ($T3) -> Void' location=Sources/main.swift:7:14 range=[Sources/main.swift:7:9 - line:7:14] field 'foo' function_ref=unapplied
    (declref_expr type='@lvalue Foo<repeat each T>' location=Sources/main.swift:7:9 range=[Sources/main.swift:7:9 - line:7:9] decl=main.(file).Foo.init(fn:).self@Sources/main.swift:4:5 function_ref=unapplied))
  (declref_expr type='(repeat each T) -> Void' location=Sources/main.swift:7:20 range=[Sources/main.swift:7:20 - line:7:20] decl=main.(file).Foo.init(fn:).fn@Sources/main.swift:5:9 function_ref=unapplied))

Score: <default 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>
Type Variables:
  $T0 as @lvalue Foo<repeat each T> @ locator@0x1350e2000 [DeclRef@Sources/main.swift:7:9]
  $T1 as @lvalue ($T3) -> Void @ locator@0x1350e2078 [UnresolvedDot@Sources/main.swift:7:14 → member]
  $T2 as Pack{repeat each T} @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T']
  $T3 [allows bindings to: pack expansion] [with possible bindings: (supertypes of) repeat each T] [defaults: repeat $T2] @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)]
  $T4 as (repeat each T) -> Void @ locator@0x1350e24b8 [DeclRef@Sources/main.swift:7:20]
  $T5 as ($T3) -> Void @ locator@0x1350e2500 [UnresolvedDot@Sources/main.swift:7:14]
Active Constraints:
  $T2 conforms to _Copyable @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T']
  $T3 can fallback to repeat $T2 @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)]
Inactive Constraints:
  repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument]
Resolved overloads:
  selected overload set choice self: $T0 == @lvalue Foo<repeat each T> for locator@0x1350e2000 [DeclRef@Sources/main.swift:7:9]
  selected overload set choice @lvalue Foo<repeat each T>.foo: $T1 == @lvalue ($T3) -> Void for locator@0x1350e2078 [UnresolvedDot@Sources/main.swift:7:14 → member]
  selected overload set choice fn: $T4 == (repeat each T) -> Void for locator@0x1350e24b8 [DeclRef@Sources/main.swift:7:20]
Opened types:
  locator@0x1350e2078 [UnresolvedDot@Sources/main.swift:7:14 → member] opens 'each T' (each τ_0_0) -> $T2
Opened pack expansion types:
  repeat $T2 opens to $T3

  (considering: $T2 conforms to _Copyable @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T']
    (simplification result:
      (removed constraint: $T2 conforms to _Copyable @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T'])
    )
    (outcome: simplified)
  )
  (considering: $T3 can fallback to repeat $T2 @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)]
    (simplification result:
    )
    (outcome: unsolved)
  )
  (Potential Binding(s):
    ($T3 [allows bindings to: pack expansion] [with possible bindings: (supertypes of) repeat each T] [defaults: repeat $T2])
  )
  (attempting type variable binding $T3 := repeat $T2
    (considering: $T3 can fallback to repeat $T2 @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)]
      (simplification result:
        (removed constraint: $T3 can fallback to repeat $T2 @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)])
      )
      (outcome: simplified)
    )
    (considering: repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument]
      (simplification result:
        (failed constraint each T same-shape $T2 @ locator@0x1350e2658 [Assign@Sources/main.swift:7:18 → function argument → pack shape])
        (removed constraint: repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument])
        (failed constraint repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument])
      )
      (outcome: error)
    )
  )

---Solver statistics---
Total number of scopes explored: 2
Maximum depth reached while exploring solutions: 2
Time: 6.790000e-01ms
---Attempting to salvage and emit diagnostics---
  (considering: $T2 conforms to _Copyable @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T']
    (simplification result:
      (removed constraint: $T2 conforms to _Copyable @ locator@0x1350e2138 [UnresolvedDot@Sources/main.swift:7:14 → member → generic parameter 'each T'])
    )
    (outcome: simplified)
  )
  (considering: $T3 can fallback to repeat $T2 @ locator@0x1350e2298 [UnresolvedDot@Sources/main.swift:7:14 → member → pack expansion type (repeat $T2)]
    (simplification result:
    )
    (outcome: unsolved)
  )
  (Potential Binding(s):
    ($T3 [allows bindings to: pack expansion] [with possible bindings: (supertypes of) repeat each T] [defaults: repeat $T2])
  )
  (attempting type variable binding $T3 := repeat $T2
    (considering: repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument]
      (simplification result:
    (attempting fix [fix: skip same-shape requirement] @ locator@0x1350e2658 [Assign@Sources/main.swift:7:18 → function argument → pack shape])
    (increasing 'applied fix' score by 1 @ locator@0x1350e2658 [Assign@Sources/main.swift:7:18 → function argument → pack shape])
        (removed constraint: repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument])
        (failed constraint repeat each T subtype $T3 @ locator@0x1350e2580 [Assign@Sources/main.swift:7:18 → function argument])
      )
      (outcome: error)
    )
  )
Sources/main.swift:7:18: error: type of expression is ambiguous without a type annotation
        self.foo = fn
        ~~~~~~~~~^~~~
error: fatalError

Environment

xedin commented 1 year ago

Note that this is not a recent regression, it doesn't typecheck with Xcode 15 beta either.

slavapestov commented 1 year ago

I think something is wrong in matchFunctionTypes(), which purportedly claims to support pack expansion types:


  // FIXME: ParamPackMatcher should completely replace the non-variadic
  // case too eventually.
  if (containsPackExpansionType(func1Params) ||
      containsPackExpansionType(func2Params)) {
    ParamPackMatcher matcher(func1Params, func2Params, getASTContext(),
                             isPackExpansionType);
    if (matcher.match())
      return getTypeMatchFailure(locator);

    for (auto pair : matcher.pairs) {
      auto result = matchTypes(pair.lhs, pair.rhs, subKind, subflags,
                               (func1Params.size() == 1
                                ? argumentLocator
                                : argumentLocator.withPathElement(
                                  LocatorPathElt::TupleElement(pair.lhsIdx))));
      if (result.isFailure())
        return result;
    }
  } else {
AnthonyLatsis commented 11 months ago

Another test case.

vanvoorden commented 9 months ago
struct Foo<each T> {
  let foo: (repeat each T) -> Void

  init() {
    let bar: (repeat each T) -> Void = { (_: repeat each T) in }
    self.foo = bar  //  Type of expression is ambiguous without a type annotation
  }
}

@AnthonyLatsis I can also repro from Xcode 15.2 and Swift 5.9.2… are there any legit workarounds for now?

slavapestov commented 9 months ago

This was fixed by #70457.

vanvoorden commented 9 months ago

@slavapestov Thanks! Looks like this is shipping in 5.10 from https://github.com/apple/swift/pull/70797?

slavapestov commented 9 months ago

Yep, a new-enough 5.10 or main developer snapshot should have the fix.

martialln commented 9 months ago

Thank you for the update @slavapestov I will test it ASAP