Open zdtorok opened 1 year ago
Reduced:
protocol P {
associatedtype A
var s: [A] { get set }
}
var p: any P
let s = p.s
We should be we rejecting this: SE-0309 is not implemented for settable storage requirements yet. You can work around it with a protocol extension:
extension TestDelegate {
func removeAllActions() {
self.actions.removeAll()
}
}
Thanks, your workaround seems to work, but what if I have to e.g. insert a new element into this array? Like the following:
protocol ActionType {
associatedtype T
var value: T? { get set }
}
protocol P {
associatedtype AType: ActionType
var actions: [AType] { get set }
func removeElements()
func addElement(_ action: AType)
}
class ActionTypeImpl: ActionType {
typealias T = Int
var value: Int?
init(value: Int? = nil) {
self.value = value
}
}
class PImpl: P {
typealias AType = ActionTypeImpl
var actions: [ActionTypeImpl] = []
func removeElements() {
actions.removeAll()
}
func addElement(_ action: ActionTypeImpl) {
actions.append(action)
}
}
class TestClass {
var pImpl: (any P)?
}
let testClass = TestClass()
testClass.pImpl = PImpl()
testClass.pImpl?.removeElements() // <-- This works
let newAction = ActionTypeImpl()
testClass.pImpl?.addElement(newAction) // <-- Member 'addElement' cannot be used on value of type 'any P'; consider using a generic constraint instead
With removeElements()
it's working because I don't pass any actual object, but is there a workaround for this case too?
As I understand it's a similar issue, at the end it cannot determine the underlying type of T
maybe, which is now an Int
?
any P
(similar to a generic parameter <T : P>
) is an abstraction over the underlying conforming type, so in itself it does not allow you to make such assumptions about the types that depend on the conforming type (associated types). The compiler does not "see" beyond the current expression when it comes to type safety, i.e., it doesn’t care that you assigned pImpl
to an instance of PImpl
earlier when type-checking the call to addElement
. Speaking of solutions, if you want to keep the run-time abstraction in order to retain the ability of assigning values of different conforming types to pImpl
, you can use a constrained existential, like so:
class TestClass<T: ActionType> {
var pImpl: (any P<T>)?
}
Note that this is a new feature that requires runtime support and it may not suit your deployment target. Though, is the regular generic approach not an option in your case?
class TestClass<T: P> {
var pImpl: T?
}
This is a tricky and subtle topic, so feel free to ask questions!
Thank you for your explanation. This constrained existential might work for my case, but after some rework of our code, it came out that we could simplify it, so at the end this level of abstraction was not needed anymore. Anyhow it's good that there would have been a solution if I really needed this. Thanks again for your help!
Good to hear! This kind of abstraction often tends to be the wrong answer.
This either regressed or was accidentally closed.
Edit: Ah, no, it just stopped crashing for a while, but has never errored out, which is what it is supposed to be doing.
Yep, have definitely run into this problem with swift 6. Will look at refactoring my code :)
Description
Compiler crashes during type checking when using protocol and associated type. Xcode gives no errors or warnings on the source code. The following code will reproduce the issue:
The stack dump:
Steps to reproduce
Simply compile the above code from command line or in a playground/Xcode.
Expected behavior
I would expect that the compiler deducts the type of
testDelegate?.actions
properly and the code builds without errors.Environment