swiftlang / swift

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

Failure to implicitly open existential on initialization through factory method #59851

Open eito opened 2 years ago

eito commented 2 years ago

Describe the bug Per the thread here: SE-0352 allows the implicit opening of existentials only when the generic type occurs exactly once in the function signature. In the sample code from the thread's first post, the init of S is implicitly using the generic type twice.

protocol P {
  associatedtype A
  func getA() -> A
}

struct S<T: P> {
    var s: T
}

func testOpenSimple(p: any P) {
  let _ = S(s: p) // **error**, Type 'any P' cannot conform to 'P'
}

An alternative approach was proposed here by @jckarter that involves a static factory method that returns the S as a protocol existential or opaque return type.

Applying this approach still yields the same error Type 'any P' cannot conform to 'P' and @jckarter thought it seemed like a bug.

To Reproduce

  1. Attempt to run the following Swift code in a Playground with Xcode 14 beta 2 (Swift 5.7)
    
    protocol P {
    associatedtype A
    func getA() -> A
    }

protocol Q {} struct S: Q { var s: T

static func make(s: T) -> some Q { return S(s: s) } }

func testOpenSimple(p: any P) { let q = S.make(s: p) // q has type any Q }


**Expected behavior**
Should build successfully and allow the initialization of `S` through the factory method resulting in an instance of `any Q` living in `q`

**Screenshots**
![image](https://user-images.githubusercontent.com/775643/176967633-5d4785db-46cb-4f2c-a9f3-0ff61706673c.png)

**Environment (please complete the following information):**
 - OS: [e.g. macOS 11.0] macOS 12.4/iOS 16.0
 - Xcode Version/Tag/Branch: 14 beta 2

**Additional context**
Kyle-Ye commented 2 years ago

A temporary workaround

func testOpenSimple(p: any P) {
    // Error
    //  let q = S.make(s: p) // q has type `any Q` 

    func bridge2<T: P>(_ s: T) -> some Q {
        S.make(s: s)
    }

    let q = bridge2(p)
}