I believe the place that's supposed to catch this is the downcast: A<Int where Int: P via M1>
and A<Int where Int: P via M2>
are intended to be distinct types, even though that's not visible in the user-visible syntax. You can see this in the expanded mangled names for makeA
in each module if you try to return the concrete A<Int>
instead of Any
:
% xcrun swift-demangle -expand s2M15makeA2M01AVyS2iAC1PAAyHCg_GyF
Demangling for $s2M15makeA2M01AVyS2iAC1PAAyHCg_GyF
kind=Global
kind=Function
kind=Module, text="M1"
kind=Identifier, text="makeA"
kind=Type
kind=FunctionType
kind=ArgumentTuple
kind=Type
kind=Tuple
kind=ReturnType
kind=Type
kind=BoundGenericStructure
kind=Type
kind=Structure
kind=Module, text="M0"
kind=Identifier, text="A"
kind=TypeList
kind=Type
kind=Structure
kind=Module, text="Swift"
kind=Identifier, text="Int"
kind=TypeList
kind=RetroactiveConformance
kind=Number, index=0
kind=ConcreteProtocolConformance
kind=Type
kind=Structure
kind=Module, text="Swift"
kind=Identifier, text="Int"
kind=ProtocolConformanceRefInOtherModule
kind=Type
kind=Protocol
kind=Module, text="M0"
kind=Identifier, text="P"
kind=Module, text="M1"
kind=AnyProtocolConformanceList
$s2M15makeA2M01AVyS2iAC1PAAyHCg_GyF ---> M1.makeA() -> M0.A<Swift.Int>
% xcrun swift-demangle -expand s2M25makeA2M01AVyS2iAC1PAAyHCg_GyF
Demangling for $s2M25makeA2M01AVyS2iAC1PAAyHCg_GyF
kind=Global
kind=Function
kind=Module, text="M2"
kind=Identifier, text="makeA"
kind=Type
kind=FunctionType
kind=ArgumentTuple
kind=Type
kind=Tuple
kind=ReturnType
kind=Type
kind=BoundGenericStructure
kind=Type
kind=Structure
kind=Module, text="M0"
kind=Identifier, text="A"
kind=TypeList
kind=Type
kind=Structure
kind=Module, text="Swift"
kind=Identifier, text="Int"
kind=TypeList
kind=RetroactiveConformance
kind=Number, index=0
kind=ConcreteProtocolConformance
kind=Type
kind=Structure
kind=Module, text="Swift"
kind=Identifier, text="Int"
kind=ProtocolConformanceRefInOtherModule
kind=Type
kind=Protocol
kind=Module, text="M0"
kind=Identifier, text="P"
kind=Module, text="M2"
kind=AnyProtocolConformanceList
$s2M25makeA2M01AVyS2iAC1PAAyHCg_GyF ---> M2.makeA() -> M0.A<Swift.Int>
(specifically, notice the ProtocolConformanceRefInOtherModule a few lines from the end in each expansion)
The Any sidesteps the link error by keeping the real fully-qualified name of A<Int>
out of the signature. But that means the as!
likewise should have a fully-qualified A<Int via M1>
or A<Int via M2>
that it's targeting (when I compile your example I get true
and false
in the printout, so I guess it picked M1), and the runtime should take that into account when it does a downcast. Even though it might make downcasts slower.
That said, in the concrete case this ought to also be detectable at compile time rather than link time: if the swiftmodule files for M1 and M2 recorded which conformance was used, M3 would be able to tell that the types don't match.
I don't know/remember if it's possible for the uses in M1 and M2 to actually get mixed up due to runtime caching of what A<Int>
is, or which conformance to use for Int: P
, but in the model I believe the only thing that does global lookups of conformances is downcasts to protocol types (any
types). To actually support that model well we'd need a way to communicate A<Int where Int: P via M1>
to users, though, and possibly even a way to spell it in source. @Joe_Groff has talked about explicitly supporting that before (presumably much like your scoped conformances), but as far as I know very little work has actually been done in that direction.