swiftlang / swift

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

[SR-2104] Calling through to other ObjC methods in a subscript shouldn't require reifying the generic params #44712

Open jessesquires opened 8 years ago

jessesquires commented 8 years ago
Previous ID SR-2104
Radar None
Original Reporter @jessesquires
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 2 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: cb424a02266daae33d6db3d626d1d262

Issue Description:

The warnings "extension of a generic Objective-C class cannot access the class's generic parameters at runtime" seem to be too strict

See discussions here:
https://twitter.com/jesse_squires/status/755063937805148160
https://twitter.com/slava_pestov/status/755061319456260096

Fails to compile:

private extension Cache {

    subscript(key: KeyType) -> ObjectType? {
        get {
            return object(forKey: key)
        }
        set {
            if let value = newValue {
                setObject(value, forKey: key)
            } else {
                removeObject(forKey: key)
            }
        }
    }
}

Version 8.0 beta (8S128d)

jessesquires commented 8 years ago

/cc @slavapestov @jckarter – not sure if this is the best title/description

jckarter commented 8 years ago

I think this is related to subscripts (unless you're seeing the same in regular methods too).

jessesquires commented 8 years ago

I'm seeing this in regular methods, too. Generally speaking, I'm running into a lot of issues with ObjC classes that have newly added lightweight generics. In particular: `NSFetchRequest` and `NSFetchedResultsController`

jessesquires commented 8 years ago

Another failing example:

extension NSFetchedResultsController {

    public func items(inSection section: Int) -> [ResultType]? {
        return sections?[section].objects as! [ResultType]?
    }

}
jckarter commented 8 years ago

Ah, that might be a related issue with bridged types such as Array. The bridging does require reified generic params as currently implemented; we haven't correctly implemented it in a way that avoids that.

swift-ci commented 8 years ago

Comment by Marcas Burnett (JIRA)

Same issue with PHFetchResult in PhotoKit:

extension PHFetchResult {
public func toArray\<T>() -> [T] {
var array = [T]()
array.reserveCapacity(count)

// can't use trailing closure syntax due to compiler complaints of ambiguity due to method renaming in Swift 3
enumerateObjects({ (obj, _, _) in
array.append(obj as! T)
})

return array
}
}

Error on func declaration line: Extension of a generic Objective-C class cannot access the class's generic parameters at runtime.

1) This is a compile time error, not a run time one, and I'm not casting the type at all.
2) I'm not even accessing the generic type from objc in the signature or as the array type.
3) When rewritten not to be a generic function at all (just return [PHObject]), it still fails to compile, giving the same error.

jckarter commented 8 years ago

mburnett (JIRA User) In that case the error is correct. Building [T] and performing the as! T cast both require having the type metadata for T available at runtime.

swift-ci commented 8 years ago

Comment by Marcas Burnett (JIRA)

@jckarter I actually played around with it some more: I changed array to be [PHObject] and the function to return the same, commented out the array.append(...) line altogether and still had the same error. I changed PHObject to Any: same error. By process of elimination, I actually found out that it's the use of enumerateObjects( _ : ) that causes the error. The block parameter's type uses ObjectType, PHFetchResult's generic parameter. When I remove the call to enumerateObjects( _ : ), the error disappears.

However, I'm still at a loss as to what to do. This worked fine in Swift 2.2 (or iOS SDK v9). It seems like introducing more type information in iOS 10 has made it impossible to call this method from Swift at all. Furthermore, after the renaming of SDK methods in Swift 3, the compiler complains of ambiguity when calling this method with trailing closure syntax.

swift-ci commented 8 years ago

Comment by Marcas Burnett (JIRA)

Also, in the previous version of the function, T was my own generic parameter, not ObjectType which is declared as a type parameter of PHFetchResult. So it seems like the type metadata for T would be available, since T was declared in Swift. I don't understand why introducing my own generic parameter necessitates knowledge of the type's generic parameter, but I'm sure I'm missing something.

jckarter commented 8 years ago

mburnett (JIRA User) I may have misinterpreted the code. In any case, being able to call enumerateObjects should be fine. The ambiguity sounds like a separate bug that deserves filing, if you haven't already.

You can try casting away the generic parameter on PHFetchResult by saying let nongenericSelf = self as! PHFetchResult<PHObject> first thing, which should allow operations on `nongenericSelf` without requiring PHFetchResult's generic parameter info.

swift-ci commented 8 years ago

Comment by Marcas Burnett (JIRA)

I will file one.

Your suggestion made sense and looked promising until I typed this much and got the same error:

extension PHFetchResult {
func toArray\<T>() -> [T] {

}
}

I guess I will just make a utility class that takes the specialized type of PHFetchResult for now. Thanks for the help.