KeyPath @dynamicMemberLookup does not compile using implicit self

It seems you cannot use implicit self when using dynamic member keypaths. This is a departure from conventional Swift code and appears to be a bug in the compiler. See the error below:

public protocol TestProtocol {
    var str1: String { get }
}

@dynamicMemberLookup
open class BaseClass<ProtocolType> {
    
    public let proto: ProtocolType
    
    init(proto: ProtocolType) {
        self.proto = proto
    }
    
    public subscript<T>(dynamicMember keyPath: KeyPath<ProtocolType, T>) -> T {
        return proto[keyPath: keyPath]
    }
}

public final class TestClass: BaseClass<TestProtocol> {
    func test() {
        print(self.proto.str1) // compiles
        print(proto.str1)      // compiles
        print(self.str1)       // compiles
        print(str1)            // ERROR: Use of unresolved identifier 'str1'
    }
}
1 Like

Perhaps the current behavior is desired because it's slightly safer? Otherwise, any symbol will default to dynamic member lookup if not defined in the current scope, which may lead to unexpected runtime failures especially for string-based dynamic member lookup.

Regardless, I think the compiler should produce a fix-it upon undefined symbols when the type of self is @dynamicMemberLookup so that the user knows what's going on.

I don't think there would be any safety implications here, since accessing a random string like self.str12345 fails to compile when it's not defined. That this only has the dynamicMember keyPath implementation means the compiler should understand both self.str1 and str1.

I would say this is a compiler bug. It's worth filing on bugs.swift.org, if you haven't yet.