-
-
Save manicaesar/984ea25673b5dc412ce3fb05ea4deaa2 to your computer and use it in GitHub Desktop.
struct StructWithTwoObjects { | |
let object: String | |
/* | |
var object: A { // Stupid, but causes compilation error - OK! | |
return self | |
} | |
*/ | |
} | |
// --------------------- | |
protocol SomeProtocol {} | |
protocol ObjectProviding { | |
associatedtype ObjectType: SomeProtocol | |
var object: ObjectType { get } | |
} | |
extension ObjectProviding where | |
Self: SomeProtocol, // It is not needed, can be commented out | |
Self.ObjectType == Self { | |
var object: ObjectType { | |
return self | |
} | |
} | |
struct ObjectContainer: SomeProtocol, ObjectProviding { | |
let object: String | |
init(object: String) { | |
self.object = object | |
} | |
} | |
struct ObjectPrinter<T: ObjectProviding> { | |
let objectContainer: T | |
init(objectContainer: T) { | |
self.objectContainer = objectContainer | |
} | |
func printTypeOfContainedObject() { | |
return print(type(of: objectContainer.object)) | |
} | |
} | |
let objectContainer = ObjectContainer(object: "string") | |
let printer = ObjectPrinter<ObjectContainer>(objectContainer: objectContainer) | |
print(type(of: objectContainer.object)) // Prints: String | |
printer.printTypeOfContainedObject() // Prints: ObjectContainer |
Ok, I have found this thread: https://forums.swift.org/t/concerned-about-this-protocol-extension-conformance-bug/9687
And I guess we can assume Ben Cohen's response is official explanation (not a bug, if we want to change it, it is a feature request 😀)
https://forums.swift.org/t/concerned-about-this-protocol-extension-conformance-bug/9687/5
Though looks like this never got enough traction to at least provide "near-miss warning" from the compiler.
O wow! That's a super interesting thread, glad you found it ❤️
While it's (a little) reassuring that this is expected and well-defined behavior, I don't think I personally like the potential implications of all of this, especially when we move this discussion to consider cross-framework implications.
Since in your merged sample we see that we can force different value
to be used by giving some hints to the compiler than this becomes possible :
// Assume this is in External Module
protocol Provider {
var value: Int { get }
func fvalue() -> Int
}
extension Provider {
var value: Int { 0 }
}
// -------
// And now in Our Module
struct Struct: Provider {
var value: String { "1" }
// Provider conformance
func fvalue() -> Int { return 1 }
}
func takeInt(_ i: Int) { print(i) }
takeInt(Struct().value)
This can be unexpected from the External Module
consumer perspective - the compiler obviously told them that they need to implement func fvalue()
to fulfil the requirements but in no other way hinted them that there are other requirements that have default values - so they aren't aware that they "silently" have an Int
property and would (rightfully so!) expect this to fail, or maybe assume that there is a conversion happening under the hood (conversion from Int
to String
would be more natural, but still)
Not to mention the samples that Xiaodi Wu and Marc Palmer mentioned in their posts 🤯
It can easily lead to a whole lot of errors that may be confusing to track down.
Many thanks for the thorough investigation.
It is interesting that the issue can be reproduced with the simple struct & protocol example you provided. I assumed the same as you - that it would not compile - and didn't even try to simplify the example further - thanks for proving me wrong! 😀
Yeah, it reminded me of the behaviour you mentioned at the end of your comment as well... but then I realised it is a little bit different, because property signatures (if there is such thing as property signature, not sure 😅) in terms of returned type are different, while
foo
andbar
are the same in terms of returned type and parameter types.I think the culprit here is that from SIL perspective, getters are equivalent to parameterless methods (more on that below). So if we modify the example to use parameterless functions instead of getters, then I guess you won't be surprised that the code below compiles - as method overloads are legal:
Now, why I think that:
?
Well, merging the two examples together into:
and looking at the SIL generated, it basically almost the same for
value
andfvalue
, e.g.:So yeah, I guess the best idea would be to ask on Swift forums whether this is actually expected behaviour (as I am not going to read compiler's code either 😝). Thanks again and I am glad you enjoyed diving into this topic 👍