Why a protocol value can be tried to downcast to any type without compiler warnings?

Consider the next code.

// Typecasting from Classes And Protocols

class Vegetable {}
class Potato: Vegetable {}
class Chips: Potato {}

protocol Vegetableable {}
protocol Potatoable: Vegetableable {}
protocol Chipsable: Potatoable {}

class Tree {}
protocol Treeable {}

struct CheckCasting {
    
    class Bee {}
    
    func get(c: Vegetable) {
        if c is Chips { print("is Cips") }
        if c is Tree { print("is Tree") } // Cast from 'Vegetable' to unrelated type 'Tree' always fails
    }
    func get(p: Vegetableable) {
        if p is Chipsable { print("is Chpsable") } // No warnings, is right
    }
    func get(t: Treeable) {
        if t is Chipsable { print("is Chipsable") } // No warnings ?
        if t is Bee { print("is Bee") } // No warnings ?
    }
}

I mean, that compiler knows, about in my last method func get(t: Treeable), the Treeable not inherited from Vegetableable and Bee not conformed to the any protocol at all. Where is my warnings?

I mean, that compiler knows, about in my last method func get(t: Treeable), the Treeable not inherited from Vegetableable and Bee not conformed to the any protocol at all. Where is my warnings?

You don't get any warning because:

  • a value can be both Treeable and Chipsable:

    struct Foo: Treeable, Chipsable { ... }
    
  • a value can be both Treeable and Bee:

    // With a subclass:
    class TreeableBee: Bee, Treeable { ... }
    
    // Or with an extension on Bee:
    extension Bee: Treeable { ... }
    

As you see, the compiler can't emit any warning, because other pieces of code in your app may mix protocols and classes together: those checks are totally valid.

3 Likes

Thanks. But in the current point of time, we don't have extensions which conforms Bee to the Treeable.

Sure. Yet the compiler has no way to know that you don't plan to do it later.

I really like that grammar police on the forum. Thank you.

I think the compiler could/should warn for non-public types/protocols!

1 Like

Or, in other words, the compiler doesn't know what the concrete type passed as Treeable will be beforehand. While it might be sometimes possible to diagnose «this cast fails for all method invocations we see» under strong accessibility constraints, the criteria, benefits and even the meaning of that diagnostic message are rather opaque.

1 Like

I agree that we would all benefit from tools that reveal, generally speaking, "unused code".

There are unused methods, unused types, etc. But we also have a subtle but valid case of "unused code" here.

Unless I'm mistaken, I think there is a consensus that such tools should belong to linters, not on the default toolchain. I admit I did not search the web for such tools yet!

Sorry, my answer was too short. I meant the warning for the "if is" that is never true because there is no conformance for the non-public structs and protocols in the example above:

func get(t: Treeable) {
    if t is Chipsable { print("is Chipsable") } // expected-error: Will never be executed
    if t is Bee { print("is Bee") } // expected-error: Will never be executed
}

That is IMHO out of reach for a linter.

Is it? We have powerful source analysis tools with SourceKit, libSyntax, and Language Server Protocol. One can build a lot of things on top of those.

I think you need to also consider the process of writing code here, not just the process of compiling a completed and fully-functional codebase. It might be annoying to get a bunch of warnings or errors just because you've written the protocol part before creating your conforming types. Or because you've considered future code evolution where you might have a type that conforms to Treeable and Chipsable, even though you don't plan to implement one in the immediate future.

2 Likes