swiftlang / swift

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

Opaque types as inferred associated types violate type safety #73245

Open JadenGeller opened 2 months ago

JadenGeller commented 2 months ago

Description / Reproduction

The following ought to generate a type-error where whoops is declared, but instead it complies and violates type safety, interpreting memory as incorrect type.

protocol ExampleProtocol {}
extension Int: ExampleProtocol {}
extension String: ExampleProtocol {}

struct ExampleFactory {
    func makeExample() -> some ExampleProtocol { 0 }
}

protocol MakeExampleProtocol {
    associatedtype Example
    func makeExample() -> Example
}
extension ExampleFactory: MakeExampleProtocol {}

let whoops: ExampleFactory.Example = "hi"
print(whoops) // -> 26984

In the example above, the String "hi" was reinterpreted as an Int since ExampleFactor.Example is Int.

If we were to unsafeBitCast a String to Int, it would trap since the sizes don't match. Instead, we can run the following:

print(unsafeBitCast("hi", to: (Int, Int).self)) // (26984, -2161727821137838080)

As you can see, the first component matches the value of whoops, confirming that Swift in the first example silently reinterpreted whoops's initializing value as a conflicting type.

Expected behavior

Cannot convert value of type 'String' to specified type 'some ExampleProtocol' (result of 'ExampleFactory.makeExample()')

Environment

swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5) Target: arm64-apple-macosx14.0

Additional information

No response

JadenGeller commented 2 months ago

I updated the expected behavior from

Cannot convert value of type 'String' to specified type 'Int'

to

Cannot convert value of type 'String' to specified type 'some ExampleProtocol' (result of 'ExampleFactory.makeExample()')

since I don't think type inference should act as a side-channel to determine the concrete instance of an opaque type.

AnthonyLatsis commented 2 months ago

A simpler example:

// test.swift

func f() -> some Sequence { "" }
typealias A = @_opaqueReturnTypeOf("$s4test1fQryF", 0) __

let a: A = "" // OK

let b: A.Element = "" // Crash
let c: A
c = "" // error: cannot assign value of type 'String' to type 'A' (aka 'some Sequence') [cannot_convert_assign]

I suspect we are wrongly assuming somewhere that an explicit type annotation necessarily implies the opaque types it represents are declared on the variable.