swiftlang / swift

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

Swift crash when creating a concrete instance from a protocol extension with an associated type #61357

Open kerrmarin opened 1 year ago

kerrmarin commented 1 year ago

Describe the bug When creating a protocol with an associated type, and extensions to that protocol that create instances of concrete implementations, and then creating instances outside of the framework that defines these extensions Swift crashes at runtime.

Steps To Reproduce Steps to reproduce the behavior:

  1. Create a protocol like:

    public protocol A {
    associatedtype B: View
    
    func build() -> B
    }
  2. Create an implementation of the protocol:
    public struct C: A {
    public func build() -> some View {
        Text("Hello")
    }
    }
  3. Create a convenience static function to make instantiating these implementations more convenient
    public extension A where Self == C {
    static func c() -> some A { //crash does *not* happen if this returns `C` instead of `some A`
        C()
    }
    }
  4. Instantiate some A (or an array [any A])from a different framework(?) for example from a test target:

    final class BugTests: XCTestCase {
    func testExample() throws {
        let c: some A = .c()
    }
    
    func testExample2() throws {
        let someCs: [any A] = [.c()]
    }
    }
  5. Swift crashes at runtime around:
    0x100e673ac <+16>:  stur   xzr, [x29, #-0x8]
    0x100e673b0 <+20>:  stur   xzr, [x29, #-0x10]
    0x100e673b4 <+24>:  adrp   x0, 5
    0x100e673b8 <+28>:  add    x0, x0, #0x118            ; demangling cache variable for type metadata for <<opaque return type of static Bug.A< where τ_0_0 == Bug.C>.c() -> some>>.0
    0x100e673bc <+32>:  bl     0x100e67420               ; __swift_instantiateConcreteTypeFromMangledName at <compiler-generated>
    0x100e673c0 <+36>:  stur   x0, [x29, #-0x20]
    ->  0x100e673c4 <+40>:  ldur   x8, [x0, #-0x8]
    0x100e673c8 <+44>:  stur   x8, [x29, #-0x30]
    0x100e673cc <+48>:  ldr    x8, [x8, #0x40]
    0x100e673d0 <+52>:  lsr    x8, x8, #0
    0x100e673d4 <+56>:  add    x8, x8, #0xf

Expected behavior I expect the test to pass, or for the compiler to complain about a mistake I've made

Environment (please fill out the following information)

Additional context This does not happen if, instead of some A in the extension you return the concrete type C. Example that reproduces this here: https://github.com/kerrmarin/swift-bug-29-sept

rraphaeldev commented 1 year ago

Slightly simpler case:

protocol A {
    func value() -> Int
}

struct B: A {
    func value() -> Int { 100 }
}

extension A where Self == B {
    static func b() -> some A {
        B()
    }
}

func printValue(_ value: any A) {
    print("value: \(value.value())")
}

// In another file
printValue(.b()) // crash

Might be worth saying that there is a couple of cases where it doesn't crash:

extension A where Self == B {
    static var b: some A {
        B()
    }
}

// In another file
printValue(.b) // doesn't crash.
AnthonyLatsis commented 1 year ago

@xedin I know a generic parameter can be inferred via calling a static protocol extension member with leading dot syntax if said member returns Self and Self is concrete under the member’s generic sig (SE-0299), but this? Do we expect it to survive Sema? Or the following seemingly related example where the same-type constraint is required but not used as an inference source:

protocol P {}
struct B: P {}
struct C: P {}

extension P where Self == B {
  static func c() -> C { C() }
}

func test<T: A>(_: T) {}

test(.c()) // 'T := C'?! 
tomsci commented 8 months ago

I'm hitting the same situation as @rraphaeldev described, trying to define a helper function on a concrete impl of a protocol to return a some MyProtocol. Although when I tried a minimal repro, I got a crash at __swift_allocate_boxed_opaque_existential_1 when the helper was called, instead of a __swift_instantiateConcreteTypeFromMangledName earlier (I mention this mostly so anyone searching for that symbol will also find this issue).

Might be worth saying that there is a couple of cases where it doesn't crash:

I would add to this, it also doesn't crash if you change func b() -> some A to func b() -> B. Which makes me think it's quite similar to #68733. Basically every time I try and use some Foo to try and hide a type, I hit a swift bug.

vanvoorden commented 3 months ago

I hit something similar in another project… I believe this also looks similar to https://github.com/apple/swift/issues/69057.