New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-3871] Protocol passing via objective-c / Any can't be cast back to protocol type #46456
Comments
@jckarter, does this sound familiar? deanWombourne (JIRA User), if you could attach a test project that would be helpful. Your instructions are probably clear enough for us to reproduce it but attaching a sample project is the best way to ensure that we're looking at the same thing. If you don't have time that's okay, though. |
Yes, this is an instance of the general "runtime dynamic casting doesn't bridge if necessary to bridge to a protocol" bug. Since sender is seen as |
Comment by Sam Dean (JIRA) @belkadan Hi - I've attached an example project - just tap the button in the ui and an assert will fail where my issue is. You can almost certainly reduce it down into a single test case, but I'm not confident enough to do that 🙂 If you need anything else, let me know. |
Here's a minimal example that reproduces the same problem: protocol P {}
struct S : P {}
let val : Any = S() as AnyObject // bridge to Obj-C as a _SwiftValue
print(val as? P) // nil Interestingly, it does work if you first cast to AnyObject: print(val as AnyObject as? P) // S() I guess the bridged type is checked when it's statically typed as AnyObject? |
@hamishknight Yeah, that sounds like a real possibility. The runtime has had a number of bugs where it only attempts certain conversions to one level of depth, but not after performing other conversions (so AnyObject -> bridge -> Protocol might work, but Any -> AnyObject -> bridge -> Protocol doesn't). It ought to work, at any rate. |
This is becoming the most painful issues of all times. I'm working with |
Found the same (or similar) issue when converting to Swift 3.1 Example here |
@tapi That's a different bug. The `ImplicitlyUnwrappedOptional` should get implicitly changed to normal `Optional` before ending up in an `Any`. Mind filing a separate bug for that? |
Comment by Vyacheslav Dubovitsky (JIRA) Not sure this is the same problem or not, but you actually can't cast a class (no matter @objc or not) to @objc protocol. Even through the AnyObject hack: @objc protocol Foo {}
class A: NSObject {}
let a = A()
print(a as? Foo) // nil
print(a as! Foo) // Could not cast value of type ... |
exevil (JIRA User) |
Comment by Vyacheslav Dubovitsky (JIRA) @hamishknight : Yeah, I see. Thanks! |
Self contained example: import Foundation
protocol P {}
struct S: P {}
class Wrapper {
@objc dynamic let any: Any = S() //dynamic ensures the struct will be boxed into _SwiftValue
init() {}
}
print(Wrapper().any is P) // false :(
print(Wrapper().any as AnyObject is P) // workaround: true :) |
Comment by JP Wright (JIRA) I just experienced this bug in a tests for a pure swift library with no NSObject subclasses present anywhere. Sure enough, casting to {{AnyObject}} first does solve the problem but I find it quite strange. Is this really an issue with bridging to Objective-C? |
It's an issue with a struct value being converted to AnyObject and then to Any. It's not strictly about Objective-C. |
Comment by Jason R Tibbetts (JIRA) I can confirm that this is still an issue in Xcode 10.2 playgrounds. |
Comment by Diogo Autilio (JIRA) Still an issue in Xcode playgrounds version 11.2.1 (11B500). |
Should be fixed by this PR: #28835 (currently in review) Note that I'm not at all sure the PR will fix all of the duplicates here. All of them involve casting to a protocol and from a nested set of containers (Any/AnyObject/Optional/Obj-C boxing), but there are a lot of paths through the dynamic casting code that handle subtle variations in different ways. |
Comment by Jason Bobier (JIRA) I can verify that this is still a problem in 11.4.1. |
If you are still seeing this, please indicate the OS version as well and if possible, a short piece of code that shows the problem. The fix mentioned above is part of the standard library code distributed with Apple operating systems, so merely using a new compiler may not demonstrate any difference. |
Comment by Jason Bobier (JIRA) OS is macOS 10.15.4 (19E287) It might be related to crossing a framework boundary. I moved the function directly into a playground and it worked, but if I built it into a framework and then called it from the playground it didn't work. It's as simple as this (this isn't the exact code, but approximately): protocol P { }
struct S<T> {
let t: T
}
extension S: P { }
func string(for obj: Any?) -> String? {
guard let obj = obj as? P else {
return nil
}
return "test"
}
let s = string(for: S(t: 0)) |
jasonbobier (JIRA User) Funny you mention the framework boundary. I was head-aching over a different (what seems like) regression SR-12610 with auto-linked static framework. Wondering if there's a deeper issue with linking in general? |
Comment by Jason Bobier (JIRA) Interesting. It is possible that this is a regression too because I was porting some old code to Swift 5.2 when this issue appeared in my unit tests. I went back and looked at the old code and it didn't need the intermediary cast to AnyObject for the unit test to succeed. |
Comment by Jason Bobier (JIRA) Looks like it is related to inheritance from obj-c. Here is a sample project showing the failure. |
I can still reproduce the problem with the example posted previously by @hamishknight: protocol P {}
struct S : P {}
let val : Any = S() as AnyObject // bridge to Obj-C as a _SwiftValue
print(val as? P) // nil Xcode: 11.4.1 (11E503a) |
Comment by Jason Bobier (JIRA) I have verified that the test case that I attached above now works on macOS 11 and Xcode 12.0 beta 4. |
Comment by Jason R Tibbetts (JIRA) Thanks for confirming this. It’s too late for us to go back and un-refactor all of our code that exhibited the bug, but it’s good to know that it won’t be an issue going forward. |
Thanks for confirming! I'll mark this as "Resolved". If people are still seeing variations of this problem (in macOS 11), please file a new bug report and we'll look into it. |
As mentioned in the comments, I'm not entirely certain all of the other issues that were duplicated here are fixed (there are a lot of paths through the code in question). For anyone encountering a similar issue, here are a few critical things to watch for:
|
I still seem to be experiencing this issue on iOS 14/XCode 14.2/Swift 5/MacOS 12.5. Is there a setting in my Xcode project I need to enable to bypass this issue? |
@tmaly1980 Can you please provide a specific example of what you're trying to do and how it's failing? |
Attachment: Download
Environment
Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
Target: x86_64-apple-macosx10.9
Additional Detail from JIRA
md5: 0252e1a81c162f1d23e390d03e7e44bc
is duplicated by:
Issue Description:
I have a public protocol
And I implement this in a module, with a public method which return an instance.
Then, in my view controller, I trigger a segue with that object as the sender
All good so far. The problem is in my prepare(for:sender:) method.
However, the cast of instance to MyProtocol always returns nil.
Using
po sender!
in the console will output the correct type.The text was updated successfully, but these errors were encountered: