swiftlang / swift

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

AnyHashable type-erasing behaves differently on Linux #77508

Open tomsci opened 1 week ago

tomsci commented 1 week ago

Description

On the linux build of Swift, AnyHashable does not appear to have the type-erasing semantics that it does on macOS and iOS. On those platforms, any type of integer converted to an AnyHashable can then be converted to any other type of integer (providing it fits), whereas on linux, you can only convert it back to the original type.

Reproduction

let i: Int32 = 1
let a = i as AnyHashable
let oi = a as? Int8 // This conversion succeeds on macOS/iOS, fails on linux

Expected behavior

Expected the as? Int8 to succeed and return Optional<Int8>(1) on all platforms. It only succeeds on macOS and iOS, and returns nil on linux.

Environment

Swift version 5.10.1 (swift-5.10.1-RELEASE) Ubuntu 22.04.5 LTS

Additional information

Tested using the following github actions job: https://github.com/tomsci/LuaSwift/pull/7 The issue was found during the development of https://github.com/tomsci/LuaSwift/pull/6 which necessitated a lot of additional code to compensate for the difference in behaviour of AnyHashable.

tbkka commented 1 week ago

I believe this is a side-effect of Foundation NSNumber support. NSNumber supports casting to/from every numeric type, and as a side-effect, allows certain other casts to do the same.

tomsci commented 1 week ago

Yikes that's a pretty big side effect, modifying the entire runtime's behaviour to support a single Foundation type! It's not like one can avoid using Foundation (in any non-trivial project) and Foundation does exist on Linux but doesn't exhibit this behaviour (I ran the same unit tests on both platforms, explicitly importing Foundation on both) so is the bug here that the linux implementation doesn't faithfully replicate this quirk?

If I'm understanding that right, it's very weird to hear that the way the language is supposed to behave isn't the way it actually behaves in 99% of projects. Is this formalised somewhere? Is the NSNumber modifying other casts behaviour considered a bug? It makes me wonder, why are integers castable to and from NSNumber in Swift? Toll-free-bridging made sense in Objective-C to and from CoreFoundation but I can't really think of any reason why I'd want this in Swift code...?

tbkka commented 1 week ago

Differences between Foundation on Linux and macOS should be reported against Foundation, I think. CC: @parkera