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
[SR-13465] Generics and hierarchical protocols don't work as expected #55907
Comments
Comment by Sean Eric Fagan (JIRA) Hm, any hierarchies don't work, and this does seem like a pretty major problem. {{}} |
@swift-ci create |
Function overloads are always resolved at compile time. Therefore if you call doSomething within Person.doCheck, the compiler selects the Base version, because at compile time it cannot know what T is. If you want dynamic dispatch at runtime, the function needs to be a protocol function (and not a global function). |
Comment by Sean Eric Fagan (JIRA) Two responses: First, if you use derived classes instead of generics in my example, it works exactly as expected. That is, the subclass object go to a subclass-specific function, the base-class object goes to a base-class specific function. Second, protocol functions –- unless I do not understand what you mean by the term – do not work either, as long as generics are involved. I in fact discovered this in a project that used protocols and generic classes and functions, and imagine just how much fun it was to discover that it was simply not possible to have hierarchical protocols, complete with extensions to implement default functions, and then find out that the classes I wrote that used them as generics would only use the one protocol or class I used in writing the generic. So, to summarize: if you use classes and subclasses, then everything works exactly as one expects. If, however, you use generics, then it will always use exactly the class/protocol you specify in the generic, even when the object is a derived class, or if the object is constrained to derived protocols. And you are not allowed to use the same name but with different protocol constraints when writing generic classes. Please, if I've missed something obvious, show me. I spent a long time winnowing down the test case to the small attached project, feel free to show how it could be done. |
Comment by Sean Eric Fagan (JIRA) (Oh, and to point out: how I discovered this was in trying to use SwiftUI with generics – having one view for protocol Base, and another for protocol Derived. But that doesn't work. And it's not possible to check for protocol conformance at run-time, so instead I have to check for specific types at runtime, which means that as I add more back-ends for my data source, I have to check for each and everyone of them. Instead of simply saying ObjectDetalView(object: self.object), and letting the compiler and runtime decide whether that's an Album-protocol only, or a Tape-protocol, or a CD-protocol. Etc.) |
Comment by Sean Eric Fagan (JIRA) Ok, so by protocol function, you mean something like this?
(Sorry, I don't seem to have figure out how to format text here very well.) That does what I expect, if that's what you meant. Now, I'm not sure how it applies to the bigger problem I was running into, but at least it's less of a surprise. |
Yes, that's what I meant. |
Attachment: Download
Environment
macOS, Xcode 11.6 and later.
Additional Detail from JIRA
md5: ac544037016382dfdb9161ffec028f5e
Issue Description:
In rewriting a medium-sized project, since my first attempt got too complicated when trying to deal with back-ends with incompatible requirements (er, as an example: Core Data and Firebase need to have different classes), I tried using protocols. That's their purpose, after all, to allow code to work on unrelated classes, as long as the classes conform to the right protocols. Most of my objects have a name and ID, and then some have more. So I created a hierarchical set of protocols, and then some generics to deal with them.
Only, it turns out, that the generics only dealt with the lowest protocol. That is, specifying something as someFunc<T: BaseProtocol>, when it might be given objects of BaseProtocol and objects of a descended protocol, will treat all of the objects of T as being only BaseProtocol.
I have attached an xcode project which is very stupid and dumb, but which demonstrates it (in the didSet property observer, every object given to it is of "AlbumProtocol," even the ones that are actually Tapes and use TapeProtocol).
This seems to significantly limit the uses, and also seems to run counter to principle of least astonishment. In particular, if I have overloaded functions, e.g.
func doSomething<T: AlbumProtocol>(object: T)
func doSomething<T: TapeProtocol>(object: T)
and call "doSomething(object)" inside the generic class... only the first one gets called.
The text was updated successfully, but these errors were encountered: