Skip to content

Commit fa6c3c1

Browse files
committed
Opaque types are only invariant when they involve same-type constraints.
An opaque type is only invariant with respect to the existential Self when the constraints on the opaque type involve Self. Such constraints are not expressible in the type-erased value, so treat them as invariant. This loosens the restriction on using members of protocol type that return an opaque type, such that (e.g.) the following is still well-formed: protocol P { } protocol Q { } extension P { func getQ() -> some Q { ... } } func test(p: any P) { let q = p.getQ() // formerly an error, now returns an "any Q" } However, this does not permit uses of members such as: extension P { func getCollection() -> some Collection<Self> { ... } // error } because the type system cannot express the corresponding existential type `any Collection<Self>`.
1 parent 4ec6dc1 commit fa6c3c1

File tree

5 files changed

+51
-12
lines changed

5 files changed

+51
-12
lines changed

lib/AST/Decl.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3914,10 +3914,29 @@ findExistentialSelfReferences(CanGenericSignature existentialSig, Type type,
39143914
return info;
39153915
}
39163916

3917-
// Opaque result types of protocol extension members contain an invariant
3918-
// reference to 'Self'.
3919-
if (type->is<OpaqueTypeArchetypeType>())
3920-
return SelfReferenceInfo::forSelfRef(TypePosition::Invariant);
3917+
// If the signature of an opaque result type has a same-type constraint
3918+
// that refereces Self, it's invariant.
3919+
if (auto opaque = type->getAs<OpaqueTypeArchetypeType>()) {
3920+
auto info = SelfReferenceInfo();
3921+
auto opaqueSig = opaque->getDecl()->getOpaqueInterfaceGenericSignature();
3922+
for (const auto &req : opaqueSig.getRequirements()) {
3923+
switch (req.getKind()) {
3924+
case RequirementKind::Conformance:
3925+
case RequirementKind::Layout:
3926+
case RequirementKind::Superclass:
3927+
continue;
3928+
3929+
case RequirementKind::SameType:
3930+
info |= findExistentialSelfReferences(
3931+
existentialSig, req.getFirstType(), TypePosition::Invariant);
3932+
info |= findExistentialSelfReferences(
3933+
existentialSig, req.getSecondType(), TypePosition::Invariant);
3934+
break;
3935+
}
3936+
}
3937+
3938+
return info;
3939+
}
39213940

39223941
// Protocol compositions preserve variance.
39233942
if (auto *existential = type->getAs<ExistentialType>())

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,7 @@ static Type typeEraseCovariantExistentialSelfReferences(Type refTy,
18131813

18141814
// Opaque types whose substitutions involve this type parameter are
18151815
// erased to their upper bound.
1816-
if (auto opaque = dyn_cast<OpaqueTypeArchetypeType>(type.getPointer())) {
1816+
if (auto opaque = dyn_cast<OpaqueTypeArchetypeType>(t)) {
18171817
for (auto replacementType :
18181818
opaque->getSubstitutions().getReplacementTypes()) {
18191819
if (hasErasedGenericParameter(replacementType)) {

test/Constraints/opened_existentials.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-opened-existential-types -enable-parametrized-protocol-types -enable-experimental-opaque-parameters
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-opened-existential-types -enable-parameterized-protocol-types
22

33
protocol Q { }
44

@@ -77,3 +77,22 @@ func useReverseIt(_ c: any CollectionOf) {
7777
let c = reverseIt(c)
7878
let _: Int = c // expected-error{{cannot convert value of type 'CollectionOf' to specified type 'Int'}}
7979
}
80+
81+
/// --- Opening existentials when returning opaque types.
82+
extension P {
83+
func getQ() -> some Q {
84+
let a: A? = nil
85+
return a!
86+
}
87+
88+
func getCollectionOf() -> some CollectionOf<A> {
89+
return [] as [A]
90+
}
91+
}
92+
93+
func testReturningOpaqueTypes(p: any P) {
94+
let q = p.getQ()
95+
let _: Int = q // expected-error{{cannot convert value of type 'Q' to specified type 'Int'}}
96+
97+
p.getCollectionOf() // expected-error{{member 'getCollectionOf' cannot be used on value of protocol type 'P'; use a generic constraint instead}}
98+
}

test/decl/protocol/existential_member_accesses_self_assoctype.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ do {
343343

344344
arg.invariantSelf1(0) // expected-error {{member 'invariantSelf1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
345345
if #available(macOS 10.15, *) {
346-
arg.invariantSelf1_1() // expected-error {{member 'invariantSelf1_1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
346+
_ = arg.invariantSelf1_1()
347347
}
348348
arg.invariantSelf2(0) // expected-error {{member 'invariantSelf2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
349349
arg.invariantSelf3(0) // expected-error {{member 'invariantSelf3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
@@ -392,7 +392,7 @@ do {
392392

393393
arg.invariantSelfProp1 // expected-error {{member 'invariantSelfProp1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
394394
if #available(macOS 10.15, *) {
395-
arg.invariantSelfProp1_1 // expected-error {{member 'invariantSelfProp1_1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
395+
_ = arg.invariantSelfProp1_1
396396
}
397397
arg.invariantSelfProp2 // expected-error {{member 'invariantSelfProp2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}
398398
arg.invariantSelfProp3 // expected-error {{member 'invariantSelfProp3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}}

test/type/opaque.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,10 +503,11 @@ extension OpaqueProtocol {
503503
}
504504

505505
func takesOpaqueProtocol(existential: OpaqueProtocol) {
506-
// this is not allowed:
507-
_ = existential.asSome // expected-error{{member 'asSome' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
508-
_ = existential.getAsSome() // expected-error{{member 'getAsSome' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
509-
_ = existential[0] // expected-error{{member 'subscript' cannot be used on value of protocol type 'OpaqueProtocol'; use a generic constraint instead}}
506+
// These are okay because we erase to the opaque type bound
507+
let a = existential.asSome
508+
let _: Int = a // expected-error{{cannot convert value of type 'OpaqueProtocol' to specified type 'Int'}}
509+
_ = existential.getAsSome()
510+
_ = existential[0]
510511
}
511512

512513
func takesOpaqueProtocol<T : OpaqueProtocol>(generic: T) {

0 commit comments

Comments
 (0)