Skip to content

Commit b7bdca2

Browse files
committed
Sema: Ban references to protocol extension members with opaque result types on an existential base
The substituted type of the member reference is an OpaqueTypeArchetypeType whose substitution map sends Self to the opened existential type for the base value. Sema erases opened existential types to their upper bound, but this is not a valid transformation in this case, because the 'Self' type reference is invariant. Calling this member on two different existential values would produce the same erased type, but this is wrong, because it actually depends on the concrete type stored in the existential. Fixes <https://bugs.swift.org/browse/SR-13419>, <rdar://problem/67451810>.
1 parent 5910d82 commit b7bdca2

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

lib/AST/Decl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4795,6 +4795,11 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
47954795
return info;
47964796
}
47974797

4798+
// Opaque result types of protocol extension members contain an invariant
4799+
// reference to 'Self'.
4800+
if (type->is<OpaqueTypeArchetypeType>())
4801+
return SelfReferenceInfo::forSelfRef(SelfReferencePosition::Invariant);
4802+
47984803
// A direct reference to 'Self'.
47994804
if (proto->getSelfInterfaceType()->isEqual(type))
48004805
return SelfReferenceInfo::forSelfRef(position);

test/type/opaque.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,28 @@ protocol SomeProtocolA {}
485485
protocol SomeProtocolB {}
486486
struct SomeStructC: SomeProtocolA, SomeProtocolB {}
487487
let someProperty: SomeProtocolA & some SomeProtocolB = SomeStructC() // expected-error {{'some' should appear at the beginning of a composition}}{{35-40=}}{{19-19=some }}
488+
489+
// An opaque result type on a protocol extension member effectively
490+
// contains an invariant reference to 'Self', and therefore cannot
491+
// be referenced on an existential type.
492+
493+
protocol OpaqueProtocol {}
494+
extension OpaqueProtocol {
495+
var asSome: some OpaqueProtocol { return self }
496+
func getAsSome() -> some OpaqueProtocol { return self }
497+
subscript(_: Int) -> some OpaqueProtocol { return self }
498+
}
499+
500+
func takesOpaqueProtocol(existential: OpaqueProtocol) {
501+
// this is not allowed:
502+
_ = existential.asSome // expected-error{{member 'asSome' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
503+
_ = existential.getAsSome() // expected-error{{member 'getAsSome' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
504+
_ = existential[0] // expected-error{{member 'subscript' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
505+
}
506+
507+
func takesOpaqueProtocol<T : OpaqueProtocol>(generic: T) {
508+
// these are all OK:
509+
_ = generic.asSome
510+
_ = generic.getAsSome()
511+
_ = generic[0]
512+
}

0 commit comments

Comments
 (0)