swiftlang / swift

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

[SR-9479] Subclasses of generic classes don't remember @objc names for optional protocol methods #51941

Open ahti opened 5 years ago

ahti commented 5 years ago
Previous ID SR-9479
Radar rdar://problem/30489065
Original Reporter @ahti
Type Bug
Environment macOS Mojave, both in Swift 4.2 and the 2018-12-09 5.0 snapshot.
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: cbac37027cfe1a9f7b7ec5f6d6688217

is duplicated by:

Issue Description:

I need to manually specify @objc names for optional methods in a protocol implemented by a generic superclass.

Example:

import Foundation

@objc protocol P {
    @objc(doF) optional func f() -> Int
}

class Gen<T>: NSObject, P {}
class NoGen:  NSObject, P {}

class NoGen1: NoGen {
    func f() -> Int {
        return 42
    }
}
(NoGen1() as P).f?() // 42, works

class Gen1: Gen<Any> {
    func f() -> Int {
        return 42
    }
}
(Gen1() as P).f?() // nil <----------

class Gen2: Gen<Any> {
    @objc(doF) func f() -> Int { // explicitly set objc name
        return 42
    }
}
(Gen2() as P).f?() // 42, works again
belkadan commented 5 years ago

I suspect they're not being inferred as @objc at all, but either way it's a problem! Thanks, Lukas.

belkadan commented 5 years ago

@DougGregor, did you have a Radar for this already?

belkadan commented 5 years ago

Also applies to classes nested in generic contexts, even if they themselves are not generic (see dup).

belkadan commented 5 years ago

The problem here seems to be in swift::findWitnessedObjCRequirements:

// If we have an optional requirement in an inherited conformance,
// check whether the potential witness matches the requirement.
// FIXME: for now, don't even try this with generics involved. We
// should be tracking how subclasses implement optional requirements,
// in which case the getWitness() check above would suffice.

I don't know what the context is here, though.