-
Notifications
You must be signed in to change notification settings - Fork 199
Fix protocol DefaultImpl with associated types + generic functions #584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.