Why is the compiler rejecting this conformance?

I'm confused here. Am I doing something wrong?

protocol BaseInterface {
  associatedtype Mode: Equatable
}

protocol P {}
class GenericStorage<Mode: Equatable> {}
typealias SpecialStorage<Mode: Equatable> = GenericStorage<Mode> & P

protocol StorageInterface: BaseInterface {
  associatedtype StorageA: GenericStorage<Mode>
  associatedtype StorageB: SpecialStorage<Mode>
}

enum ConcreteMode: Equatable {}

class A: GenericStorage<ConcreteMode> {}
class B: A, P {}

// rejected
struct ConcreteInterface: StorageInterface {
  typealias Mode = ConcreteMode
  typealias StorageA = A
  typealias StorageB = B
}

Here are the errors Swift playground outputs (similar setup is also rejected in a non-playground project):

error: type 'ConcreteInterface' does not conform to protocol 'StorageInterface'
struct ConcreteInterface: StorageInterface {
^

note: possibly intended match 'StorageA' (aka 'A') does not inherit from 'GenericStorage<Self.Mode>'
typealias StorageA = A
^

note: possibly intended match 'StorageB' (aka 'B') does not inherit from 'GenericStorage<Self.Mode>'
typealias StorageB = B
^

note: protocol requires nested type 'StorageA'; do you want to add it?
associatedtype StorageA: GenericStorage
^

note: protocol requires nested type 'StorageB'; do you want to add it?
associatedtype StorageB: SpecialStorage

cc @Slava_Pestov Is this a bug? I think this conformance shouldn't be rejected.

Any workaround suggestion would be awesome, I really need to get this compiling so I can extract these Storage types.

1 Like

Okay I found a workaround, but I'm still confused why the above does not work. The workaround is another pain point in my implementation because if one of the type must be public, all of them are forced to be public.

struct GenericInterface<
  Mode: Equatable,
  StorageA: GenericStorage<Mode>,
  StorageB: SpecialStorage<Mode>
>: StorageInterface {}

typealias ConrecteInterface = GenericInterface<ConcreteMode, A, B>

Filed a bug report: [SR-9706] Compiler rejecting conformance · Issue #52144 · apple/swift · GitHub

Hello Adrian,
I tried to play a bit with your code, trying to simplify it until the error disappeared.

It didn't.


protocol BaseInterface {
    associatedtype Mode: Equatable
}

class GenericStorage<M: Equatable> {}

protocol StorageInterface: BaseInterface {
    associatedtype StorageA: GenericStorage<Mode>
}

//// Rejected
//// Possibly intended match 'StorageA' (aka 'GenericStorage<ConcreteInterface1.Mode>') does not inherit from 'GenericStorage<Self.Mode>'
class ConcreteInterface1: StorageInterface {
    enum Mode: Equatable {}
    typealias StorageA = GenericStorage<Mode>
}

// Second try

class A: GenericStorage<ConcreteInterface2.Mode> {}

//// Still rejected
//// Possibly intended match 'StorageA' does not inherit from 'GenericStorage<Self.Mode>'
class ConcreteInterface2: StorageInterface {
    enum Mode: Equatable {}
    typealias StorageA = A
}

I further simplify the code until it came down to this:


class GenericStorage<M> {}

protocol StorageInterface {
    associatedtype Mode
    associatedtype StorageA: GenericStorage<Mode>
}

//// Rejected
//// Possibly intended match 'ConcreteInterface3.StorageA' does not inherit from 'GenericStorage<Self.Mode>'
class ConcreteInterface3: StorageInterface {

    enum Mode: Equatable {}
    class StorageA: GenericStorage<Mode> {}
}

At this point it definitely looks like nothing can inherit from GenericStorage<Self.Mode>.
Maybe there's an error in how associatedtype's typealiases are resolved?

2 Likes

This might be [SR-8028] Protocol with where class constraint fails to compile, but same extension succeeds · Issue #50561 · apple/swift · GitHub.

2 Likes

Yeah the code sample that @huon showed in the last comment is the exact same setup actually. Hopefully we'll see a fix in Swift 5.x. I have multiple storage types that are extended with each further subclass, but sometimes I also need to make some subclasses generic down the road to the final non-generic class.

1 Like