Cannot infer contextual base in reference

When creating a protocol that requires inheritance to a class that specialises a generic protocol, the extension implementation is lost outside of the protocol definition.

// Interface

protocol FooType: Hashable, CaseIterable { }
protocol BarType: Hashable { }

protocol Resolver {
	associatedtype Foo: FooType
	associatedtype Bar: BarType

	func bar(for foo: Foo) -> Bar
}

protocol Provider {
	associatedtype Foo: FooType
	associatedtype Bar: BarType

	typealias FooBarMap = [Foo: Bar]

	var map: FooBarMap { get }
}

extension Provider {

	subscript(_ foo: Foo) -> Bar? {
		map[foo]
	}
}

// Implementation

enum ActualFoo: FooType, CaseIterable {
	case fooOne, fooTwo
}

enum ActualBar: BarType {
	case barOne, barTwo
}

class BaseProvider: Provider {
	typealias Foo = ActualFoo
	typealias Bar = ActualBar

	let map: FooBarMap

	init(map: FooBarMap) {
		self.map = map
	}
}

let bp: BaseProvider?
bp?[.fooOne]  // works fine, no compiler errors

protocol ExtraProvider: BaseProvider {
	var firstBar: Bar? { get }
}

extension ExtraProvider {

	func foo() {
		self[.fooOne] // works fine, no compiler errors
	}
}

var ep: ExtraProvider?
ep?[.fooOne] // Cannot infer contextual base in reference to member 'fooOne'

and the error I get is:

Again, I'm not sure if my assumptions that I make about the generics are correct and this outcome is expected or the compiler is acting a bit funny.

Thank you!

If we fill in the implicit base type with what we expect the compiler to do for us, we get a different error:

ep?[ActualFoo.fooOne]  // error: cannot convert value of type 'ActualFoo' to expected argument type 'ExtraProvider.Foo'

It looks like we're not inferring that ExtraProvider.Foo will always be the same type as BaseProvider.Foo. Note that we don't need the optional to reproduce this error, the same issue occurs if ep has static type ExtraProvider.

I believe this works from within ExtraProvider.foo because self has type Self, i.e., the dynamic, concrete conforming type.

I'm not sure if there's a technical reason why we couldn't consider this valid—it seems like we should have enough information in this case to know that ExtraProvider.Foo == ActualFoo for all conforming types, but there might be something I'm missing... :thinking:

2 Likes