Fix protocol DefaultImpl with associated types + generic functions#584
Conversation
|
Given this example protocol: protocol GenericProtocol<C> {
associatedtype C
associatedtype V
func genericParameter<T>(value: T)
func genericParameterWithAssociatedType<T>(value: T, theC: C, theV: V) -> V
}Here's a diff of how it would generate before (with compile errors) vs how it generates now: ...
class DefaultImplCaller: GenericProtocol, @unchecked Sendable {
- private let reference: Any
+ private let reference: () -> any GenericProtocol<C>
- init<_CUCKOO$$GENERIC: GenericProtocol>(from defaultImpl: UnsafeMutablePointer<_CUCKOO$$GENERIC>, keeping reference: @escaping @autoclosure () -> Any?) where _CUCKOO$$GENERIC.C == C, _CUCKOO$$GENERIC.V == V {
+ init<_CUCKOO$$GENERIC: GenericProtocol>(from defaultImpl: UnsafeMutablePointer<_CUCKOO$$GENERIC>, keeping reference: @escaping @autoclosure () -> _CUCKOO$$GENERIC) where _CUCKOO$$GENERIC.C == C, _CUCKOO$$GENERIC.V == V {
self.reference = reference
- _storage$1$genericParameter = defaultImpl.pointee.genericParameter // error: Generic parameter 'T' could not be inferred
- _storage$2$genericParameterWithAssociatedType = defaultImpl.pointee.genericParameterWithAssociatedType // error: Generic parameter 'T' could not be inferred
}
- private let _storage$1$genericParameter: (T) -> Void // error: Cannot find type 'T' in scope
func genericParameter<T> (value p0: T) {
- return _storage$1$genericParameter(p0)
+. return reference().genericParameter(p0)
}
- private let _storage$2$genericParameterWithAssociatedType: (T, C, V) -> V // error: Cannot find type 'T' in scope
func genericParameterWithAssociatedType <T> (value p0: T, theC p1: C, theV p2: V) -> V {
+ func openExistential<_CUCKOO$$GENERIC: GenericProtocol<C>>(_ opened: _CUCKOO$$GENERIC) -> V {
+ return opened.genericAndAassociatedTypeParameters(value: p0, theC: p1, theV: p2 as! _CUCKOO$$GENERIC.V) as! V
+ }
+ return openExistential(reference())
- return _storage$2$genericParameterWithAssociatedType(p0)
}
}
... |
There was a problem hiding this comment.
I have some nitpicks about the code so that it fits into the rest of the project, but I wasn't able to run it locally yet (have to download iOS 26.1 runtime first). I'll finish the review then.
In the meantime, thanks for this PR! It fixes the thing I set out to do with addition of generics in the first place, but lacked the time to bring to fruition, so kudos. 🙂
|
@Brennanium is there a way that this could also cover a protocol with same-type values? E.g. protocol SpecificProtocol: GenericProtocol where C == String, V == Int {} |
|
@MatyasKriz Glad to help! 😊 I've addressed all your nitpick suggestions in a follow-up commit; let me know if you encounter any other issues when you manage to run it locally. |
|
@via-guy I looked into what it would take to get mocks generated for protocols with same-type requirements working, and unfortunately it appears to mostly be unrelated to the changes I'm making here in this PR. Tackling the problem would likely involve beefing up the |
Perfect! I haven't found any blockers, so I've just merged this and will squash and release. Thanks for the help, @Brennanium! |
Fix for issue #583 by both constraining and opening the existential type and using stored
referenceinstead of storage for generic functionsMaking
referencea non-optional constrained existential instead ofAny?type seems to work for all of the test cases I could come up with, and keeping thepointeein the autoclosure as thereferencein theenableDefaultImplementation<>(mutating:)init could possibly cause a retain cycle or something? But since we're already capturing the UnsafeMutablePointer for getters and setters anyways, it seemed like an ok thing try.