What happens when a protocol is constrained to inherit from a class?

First: the error code:

class AA { }

protocol Action where Self: AA {
    func method1()
    func method2()
}

extension Action {
    func method1() {
        print("method1")
    }
}

class List: AA, Action {
    func method2() {
        print("List method2")
    }
}

class Detail: AA, Action {
    func method2() {
        print("Detail method2")
    }
}

let controllers = [List(), Detail()] as [Any]
if let action = controllers.first as? Action {
    action.method2() //error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x2).
}

then right code , everything work fine:

class AA { }

protocol Action {
    func method1()
    func method2()
}

extension Action where Self: AA {
    func method1() {
        print("method1")
    }
}

class List: AA, Action {
    func method2() {
        print("List method2")
    }
}

class Detail: AA, Action {
    func method2() {
        print("Detail method2")
    }
}

let controllers = [List(), Detail()] as [Any]
if let action = controllers.first as? Action {
    action.method2() //printed List method2
}

Question1: What happens when a protocol is constrained to inherit from a class?

Question2: What is the difference between the right code and the wrong one?

ps: I am using Xcode 9.4 (9F1027a), Swift 4.1

Right now both of your examples crash on master. This is likely just another bug related to Self in where clauses. Both of them should produce the same result.

When a protocol declaration has a superclass constraint on Self, only subclasses of the superclass can conform to the protocol. Literally, the protocol says "some type that conforms to me and is a subclass of T".

I suppose there is no need to talk about the difference since they both crash.

@Slava_Pestov will be happy to see this ;)

Reduced example

class AA {}

protocol Action where Self: AA {
  func method1()
}

extension Action {
  func method1() {print()}
}

Leaving method1 empty prevents the crash. The location of the where clause does not matter, the direct inheritance syntax also crashes.

1 Like

Just a warning: Use where Self : SomeClass on your own risk since this feature is not really implemented and is just a side-effect of class constrained existentials. When this feature will be properly implemented the syntax will be similar to normal inheritence protocol MyProto : SomeClass. That said, I encourage you to remove such constraints and wait until this feature is officially implemented.

2 Likes

You can hack around it by writing it like this:

protocol Action: class where Self: AA {
// ...
}

(you'll get a compiler warning because :class is technically redundant)

As @DevAndArtist said, the feature is not implemented yet. The typechecker seems able to interpret the supertype constraint, but later stages of the compiler apparently don't consistently infer that those constraints also mean the objects are classes.

It's cool that you found such a reduced test-case for this, though - could you file a bug report?

the second code on Xcode 9.4 (9F1027a), Swift 4.1.2 work fine, not crash. what version of swift on master

You can try crashing it with Xcode 10 beta. The master branch doesn't have a version, it is the current state of the code base. The point is that you are encountering a bug related to a known problem.