Skip to content

Commit 3d31793

Browse files
committed
AST, Sema: Teach findProtocolSelfReferences that some stdlib collections preserve variance
* Swift.Array preserves variance in its 'Element' type * Swift.Dictionary preserves variance in its 'Value' type
1 parent bcfafc1 commit 3d31793

File tree

4 files changed

+98
-15
lines changed

4 files changed

+98
-15
lines changed

lib/AST/Decl.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4814,12 +4814,26 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
48144814
return findProtocolSelfReferences(proto, selfType->getSelfType(), position);
48154815
}
48164816

4817-
// Bound generic types are invariant.
4818-
if (auto boundGenericType = type->getAs<BoundGenericType>()) {
4817+
// Most bound generic types are invariant.
4818+
if (auto *const bgt = type->getAs<BoundGenericType>()) {
48194819
auto info = SelfReferenceInfo();
4820-
for (auto paramType : boundGenericType->getGenericArgs()) {
4821-
info |= findProtocolSelfReferences(proto, paramType,
4820+
4821+
const auto &ctx = bgt->getDecl()->getASTContext();
4822+
if (ctx.getArrayDecl() == bgt->getDecl()) {
4823+
// Swift.Array preserves variance in its Value type.
4824+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
4825+
position);
4826+
} else if (bgt->getDecl() == ctx.getDictionaryDecl()) {
4827+
// Swift.Dictionary preserves variance in its Element type.
4828+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
48224829
SelfReferencePosition::Invariant);
4830+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
4831+
position);
4832+
} else {
4833+
for (auto paramType : bgt->getGenericArgs()) {
4834+
info |= findProtocolSelfReferences(proto, paramType,
4835+
SelfReferencePosition::Invariant);
4836+
}
48234837
}
48244838

48254839
return info;

lib/Sema/CSApply.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6800,6 +6800,19 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
68006800
return result;
68016801
}
68026802

6803+
case TypeKind::BoundGenericStruct: {
6804+
auto toStruct = cast<BoundGenericStructType>(desugaredToType);
6805+
if (toStruct->getDecl() != ctx.getArrayDecl() &&
6806+
toStruct->getDecl() != ctx.getDictionaryDecl())
6807+
break;
6808+
6809+
if (toStruct->getDecl() == cs.getType(expr)->getAnyNominal())
6810+
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
6811+
locator);
6812+
6813+
break;
6814+
}
6815+
68036816
#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
68046817
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
68056818
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
@@ -6813,7 +6826,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
68136826
case TypeKind::Struct:
68146827
case TypeKind::Class:
68156828
case TypeKind::BoundGenericClass:
6816-
case TypeKind::BoundGenericStruct:
68176829
case TypeKind::Metatype:
68186830
case TypeKind::DynamicSelf:
68196831
case TypeKind::PrimaryArchetype:

test/decl/protocol/conforms/inherited.swift

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
// Inheritable: method with 'Self' in its signature
3+
// Inheritable: method with 'Self' in contravariant position.
44
protocol P1 {
5-
func f1(_ x: Self?) -> Bool
5+
func f1a(_ x: Self?) -> Bool
6+
7+
func f1b(_ x: [Self])
8+
9+
func f1c(_ x: [String : Self])
610
}
711

812
// Inheritable: property with 'Self' in its signature.
@@ -52,12 +56,13 @@ protocol P9 {
5256
static func ==(x: Self, y: Self) -> Bool
5357
}
5458

55-
// Never inheritable: method with 'Self' in a non-contravariant position.
59+
// Never inheritable: method with 'Self' in invariant position.
60+
struct G<T> {}
5661
protocol P10 {
57-
func f10(_ arr: [Self])
62+
func f10(_ arr: G<Self>)
5863
}
5964
protocol P10a {
60-
func f10a(_ arr: [Self])
65+
func f10a(_ arr: G<Self>)
6166
}
6267

6368
// Never inheritable: method with 'Self' in curried position.
@@ -91,7 +96,11 @@ protocol P15 {
9196
// non-final class.
9297
class A : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 {
9398
// P1
94-
func f1(_ x: A?) -> Bool { return true }
99+
func f1a(_ x: A?) -> Bool { return true }
100+
101+
func f1b(_ x: [A]) { }
102+
103+
func f1c(_ x: [String : A]) { }
95104

96105
// P2
97106
var prop2: A { // expected-error{{property 'prop2' in non-final class 'A' must specify type 'Self' to conform to protocol 'P2'}}
@@ -135,10 +144,10 @@ class A : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 {
135144
required init(int: Int) { }
136145

137146
// P10
138-
func f10(_ arr: [A]) { } // expected-error{{protocol 'P10' requirement 'f10' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}}
147+
func f10(_ arr: G<A>) { } // expected-error{{protocol 'P10' requirement 'f10' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}}
139148

140149
// P10a
141-
func f10a(_ arr: [A]) { } // expected-note {{'f10a' declared here}}
150+
func f10a(_ arr: G<A>) { } // expected-note {{'f10a' declared here}}
142151

143152
// P11
144153
func f11() -> (_ x: A) -> Int { return { x in 5 } }
@@ -196,7 +205,11 @@ func testB8(_ b8: B8) {
196205
// Class A9 conforms to everything.
197206
final class A9 : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 {
198207
// P1
199-
func f1(_ x: A9?) -> Bool { return true }
208+
func f1a(_ x: A9?) -> Bool { return true }
209+
210+
func f1b(_ x: [A9]) { }
211+
212+
func f1c(_ x: [String : A9]) { }
200213

201214
// P2
202215
var prop2: A9 {
@@ -231,7 +244,7 @@ final class A9 : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 {
231244
required init(int: Int) { }
232245

233246
// P10
234-
func f10(_ arr: [A9]) { }
247+
func f10(_ arr: G<A9>) { }
235248

236249
// P11
237250
func f11() -> (_ x: A9) -> Int { return { x in 5 } }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-swift-frontend -typecheck -dump-ast %s | %FileCheck %s
2+
3+
// Test that covariant 'Self' references get erased to the existential base type
4+
// when operating on existential values.
5+
6+
class C {}
7+
protocol P {
8+
func lotsOfSelfFunc(
9+
_: (Self) -> Void,
10+
_: (Self?) -> Void,
11+
_: ([Self]) -> Void,
12+
_: ([Array<Self>?]) -> Void
13+
) -> [String : () -> Self]
14+
15+
var lotsOfSelfProp: (
16+
_: (Self) -> Void,
17+
_: (Self?) -> Void,
18+
_: ([Self]) -> Void,
19+
_: ([Array<Self>?]) -> Void
20+
) -> [String : () -> Self] { get }
21+
}
22+
protocol Q {}
23+
24+
do {
25+
class C {}
26+
27+
func testCovariantSelfErasure(p: P, pq: P & Q, pc: P & C) {
28+
let x1 = p.lotsOfSelfFunc
29+
let x2 = p.lotsOfSelfProp
30+
31+
let x3 = pq.lotsOfSelfFunc
32+
let x4 = pq.lotsOfSelfProp
33+
34+
let x5 = pc.lotsOfSelfFunc
35+
let x6 = pc.lotsOfSelfProp
36+
37+
// CHECK: (pattern_named type='((P) -> Void, (P?) -> Void, ([P]) -> Void, ([Array<P>?]) -> Void) -> [String : () -> P]' 'x1')
38+
// CHECK: (pattern_named type='((P) -> Void, (P?) -> Void, ([P]) -> Void, ([Array<P>?]) -> Void) -> [String : () -> P]' 'x2')
39+
// CHECK: (pattern_named type='((P & Q) -> Void, ((P & Q)?) -> Void, ([P & Q]) -> Void, ([Array<P & Q>?]) -> Void) -> [String : () -> P & Q]' 'x3')
40+
// CHECK: (pattern_named type='((P & Q) -> Void, ((P & Q)?) -> Void, ([P & Q]) -> Void, ([Array<P & Q>?]) -> Void) -> [String : () -> P & Q]' 'x4')
41+
// CHECK: (pattern_named type='((C & P) -> Void, ((C & P)?) -> Void, ([C & P]) -> Void, ([Array<C & P>?]) -> Void) -> [String : () -> C & P]' 'x5')
42+
// CHECK: (pattern_named type='((C & P) -> Void, ((C & P)?) -> Void, ([C & P]) -> Void, ([Array<C & P>?]) -> Void) -> [String : () -> C & P]' 'x6')
43+
}
44+
}

0 commit comments

Comments
 (0)