swiftlang / swift

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

[SR-13647] @dynamicMemberLookup subscript ambiguity doesn't match closest match #56083

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-13647
Radar rdar://problem/69901108
Original Reporter freak4pc (JIRA User)
Type New Feature

Attachment: Download

Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | New Feature | |Assignee | None | |Priority | Medium | md5: ea74299f5a54ddc1c2ff715e14847409

Issue Description:

Im not sure if this is a bug or something not expressible by Swift. Feel free to close this if it doesn't make sense.

I have code similar to the following:

@dynamicMemberLookup
class Something<Base: NSObject> {
    subscript<Prop>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Prop>) -> XYZ<Prop> {
        .init()
    }
}
extension Something where Base: UIControl {
    subscript<Prop>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Prop>) -> ABC<Prop> {
        .init()
    }
}

class Example: UIControl {
    var value: String
    init(_ value: String) {
        self.value = value
        super.init(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

func tester() {
    let control = Something<Example>()

    let res = control.value
}

This code causes some unexpected (or not) disambiguation. Sometimes it matches the ABC return type and sometimes the XYZ return type. This seems to be even worse when the extension is in a different module.

I would expect:

  1. To match the "closest" class, e.g. UIControl is closer than NSObject
  2. If not, that it would always match the same thing

res recognized as ABC

auto-complete recognized as XYZ

Thank you🙂

typesanitizer commented 4 years ago

@swift-ci create

ahoppen commented 2 years ago

Reduce the issue a little bit further:

protocol MyProto {}

struct ABC {}
struct XYZ {}

@dynamicMemberLookup
class Something<Base> {
    subscript<Prop>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Prop>) -> XYZ {
        .init()
    }
}

extension Something where Base: MyProto {
    subscript<Prop>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Prop>) -> ABC {
        .init()
    }
}

class Example: MyProto {
    var value: String = ""
}

func tester() {
    let control = Something<Example>()

  let res = control.#^COMPLETE^#
}

// CHECK: Decl[InstanceVar]/CurrNominal:      value[#ABC#]; name=value