-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Passing class-constrained type as AnyObject instance behavior is not matched on Darwin and non-Darwin platform #70645
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
Comments
The dynamic Self type inside a class is always convertible to AnyObject, regardless of Objective-C interop. This is a type checker bug. |
Thanks for the explanation. And I think this should be a "Starter Bug" easy to be fixed. I have not explored with Swift's "Type Checker" code before, can you help point out the relevant code folder/scope so that I can keep investigating and hopefully fix it myself? |
Swift have such check here. But the test is marked as "REQUIRES: objc_interop" |
The conversion code is under a if check of Maybe the solution is we just move this part out of the |
I'm sorry, I misunderstood the original bug report. You're not converting an instance of a class to AnyObject -- that's allowed -- you're converting a metatype value to AnyObject. That is indeed not supported without an Objective-C runtime, because metatypes do not have a Swift reference counting header, so allowing the conversion here would lead to a runtime crash. Can you change your code to traffic in |
I believe this is actually a bug and should not be allowed on non-Objective-C platforms. @jckarter what do you think? |
I'm also curious about the runtime behavior for this on non-Objective-C platform. I'll give it a try and report later. |
Test it via the following snippets // DemoKit's main.swift
import Foundation
class X {}
func test1(_ obj: AnyObject) -> UInt64 {
UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
}
func test2() {
print("RawValue: \(test1(X.self))")
}
test2() We are expecting a stable result here. And macOS is fine on this ./DemoKit
RawValue: 4295033832
./DemoKit
RawValue: 4295033832
./DemoKit
RawValue: 4295033832
./DemoKit
RawValue: 4295033832 On Linux platform we need some patch here to make it work - print("RawValue: \(test1(X.self))")
+ print("RawValue: \(test1(X.self as AnyObject))") And no runtime crash happen. We did successfully get a result. But the result is surprisingly unstable on different launch. ./DemoKit
RawValue: 187651124585888
./DemoKit
RawValue: 187651270174112
./DemoKit
RawValue: 187650465600928
... And if we add 3 more ./DemoKit
RawValue: 187650637530528
RawValue: 187650637531488
RawValue: 187650637531488
RawValue: 187650637531488
./DemoKit
RawValue: 187651094341024
RawValue: 187651094341984
RawValue: 187651094341984
RawValue: 187651094341984 IMO, such behavior means there is something wrong. Thus we should forbids "X.self as AnyObject" on non-ObjC Platform and always return false for "X.self as? AnyObject". This may be a breaking change for non-ObjC Platform user, but it is worth to fix it on next release (5.10 or 5.10+1) IMO. (Or do we need to postpone the fix to landing on Swift 6?) |
I believe what's happening is that the explicit |
🤔 So is this expected behavior or should we somehow add a patch to ban this behavior? |
It's expected behavior, unfortunately. |
Seems like we should be able to change the metatype representation so it did look like a Swift object in memory. That would be a step towards resolving a number of other related problems with metatypes on Linux. |
I assume this is the logic you are talking about https://github.com/apple/swift/blob/a4e99b315cb3b4a39c7bf1686b907573a91f957a/stdlib/public/core/BridgeObjectiveC.swift#L796-L828 Can we at least return a stable identifier just as what it did on ObjC-supported platform? (Maybe a Hash table to track it?) |
Checking the compiled code by running We can see it was indeed calling a
|
More test case on Linux platform import Foundation
class X {}
func test1(_ obj: AnyObject) -> UInt64 {
UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
}
func test2() {
print("RawValue: \(test1(X.self as AnyObject))")
}
test2()
test2()
test2()
test2()
// Result will be x y y y import Foundation
class X {}
func test1(_ obj: AnyObject) -> UInt64 {
UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
}
func test2() {
let a = X.self as AnyObject
print("RawValue: \(test1(a))")
}
test2()
test2()
test2()
test2()
// Result will be x x x x import Foundation
class X {}
func test1(_ obj: AnyObject) -> UInt64 {
UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
}
func test2() {
let a = X.self
print("RawValue: \(test1(a as AnyObject))")
}
test2()
test2()
test2()
test2()
// Result will be x y y y |
Description
Passing class-constrained type as AnyObject instance behavior is not matched on Darwin and non-Darwin platform
On Darwin platform, we can pass it directly.
On non-Darwin platform, a fatalError will be emitted - "error: argument type 'Self.Type' expected to be an instance of a class or class-constrained type". And we can workaround it by manually adding "as AnyObject"
Reproduction
Expected behavior
The behavior is matched. (The current Darwin platform behavior is preferred.)
Environment
Linux:
swift --version
Swift version 5.9.2 (swift-5.9.2-RELEASE)
Target: aarch64-unknown-linux-gnu
macOS:
swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx14.0
Additional information
Forum Post about the issue: https://forums.swift.org/t/69190
The text was updated successfully, but these errors were encountered: