Skip to content
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-7738] Empty array type casting always succeeds #3699

Closed
swift-ci opened this issue May 22, 2018 · 4 comments
Closed

[SR-7738] Empty array type casting always succeeds #3699

swift-ci opened this issue May 22, 2018 · 4 comments

Comments

@swift-ci
Copy link
Contributor

Previous ID SR-7738
Radar None
Original Reporter romrom (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment

Tested in Xcode 9.3 / Swift 4.1

Additional Detail from JIRA
Votes 0
Component/s Compiler, Foundation, Standard Library
Labels Bug
Assignee None
Priority Medium

md5: 419ef5984a76850c45061699b14d3f04

relates to:

  • SR-6192 Spurious "Cast from ... to unrelated type ... always fails" warning when implicitly converting an empty collection

Issue Description:

Given the following snippet

protocol Bar { }

struct Foo { }

let myArray: [Foo] = []

if let _ = myArray as? [Bar] {
    print("Type casting succeeded")
} else {
    print("Type casting failed")
}

I get "Type casting succeeded" and I'm expecting the opposite.

The array is empty, however if we declare it with at least one element, like so

let myArray = [Foo()]

I get the expected behavior.

Trying to cast an empty array, whatever its type declaration is, to an array of any kind always succeeds.

Either it's a bug, or something I don't get because it is very counter-intuitive. For now my only workaround is to check for emptiness of the array

if let myArray = myArray as? [Bar], !myArray.isEmpty {
    print("Type casting succeeded")
} else {
    print("Type casting failed")
}
@swift-ci
Copy link
Contributor Author

Comment by Romain Vincens (JIRA)

What's even stranger is that if you try to cast to a concrete type (not a protocol) you get a compiler warning but it runs and behaves incorrectly

if let myArray = myArray as? [Int] {
    print("Type casting succeeded")
} else {
    print("Type casting failed")
}

--> «Cast from '[Foo]' to unrelated type '[Int]' always fails»

And you get the «Type casting succeeded» output

@hamishknight
Copy link

Related: SR-6192

@swift-ci
Copy link
Contributor Author

Comment by Romain Vincens (JIRA)

Even though it's counter-intuitive I could accept that an empty array of something can be cast in any kind of array with unrelated type.

However it can cause crashes if we're using KVC when mix and matching with ObjC, even though we have a fence on the array being of type `[Identifiable]`

import Foundation

protocol Identifiable {
    var `id`: String { get set }
}

protocol Base { }

@objc
class Foo: NSObject, Base { }

@objc
class Bar: NSObject, Base, Identifiable {
    @objc var id: String
    
    init(id: String) {
        self.id = id
    }
}

class Receiver<O: Base> {
    
    let store: [O]
    
    init(store: [O]) {
        self.store = store
    }
    
    @discardableResult
    func filterOut(_ objects: [O]) -> [O] {
        
        if let objects = objects as? [Identifiable] {
            
            let objectIds = objects.compactMap { $0.id }
            let predicate = NSPredicate(format: "NOT (id IN %@)", objectIds)

            let filtered = store.filter {
                predicate.evaluate(with: $0)
            }
            
            return filtered
            
        } else {
            
            return store
            
        }
    }
    
}

let barReceiver = Receiver(store: [Bar(id: "toto"), Bar(id: "tutu")])
print(barReceiver.filterOut([Bar(id: "toto")]))   //  [<Toto.Bar: 0x608000038540>]
print(barReceiver.filterOut([]))    // [<Toto.Bar: 0x608000036920>, <Toto.Bar: 0x608000038540>]

let fooReceiver = Receiver(store: [Foo(), Foo()])
print(fooReceiver.filterOut([Foo()]))   // [<Toto.Foo: 0x60400001c8e0>, <Toto.Foo: 0x60400001c8d0>]
print(fooReceiver.filterOut([]))    // CRASH

-> `Foo` is not KVC compliant with `id`

It's an example. The code is not very logical since we should check that `store` can be cast to `[Identifiable]` instead of the passed-in objects (that we only use for ids), but it's an example. Things like this can happen.

@LucianoPAlmeida
Copy link
Contributor

Fixed on master romrom (JIRA User) can you please test using the next available snapshot and close. Thanks

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants