Skip to content

Commit a223d37

Browse files
Merge pull request #34140 from AnthonyLatsis/coself-array
AST, Sema: Teach findProtocolSelfReferences that some stdlib collections preserve variance
2 parents d33c79e + 1ee4d5b commit a223d37

File tree

9 files changed

+124
-23
lines changed

9 files changed

+124
-23
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,8 +2938,8 @@ ERROR(dynamic_self_non_method,none,
29382938
"%select{global|local}0 function cannot return 'Self'", (bool))
29392939

29402940
ERROR(dynamic_self_invalid,none,
2941-
"covariant 'Self' can only appear as the type of a property, subscript or method result; "
2942-
"did you mean '%0'?", (StringRef))
2941+
"covariant 'Self' can only appear as the possibly optional type of a "
2942+
"property, subscript or method result; did you mean '%0'?", (StringRef))
29432943
ERROR(dynamic_self_in_mutable_property,none,
29442944
"mutable property cannot have covariant 'Self' type", ())
29452945
ERROR(dynamic_self_in_stored_property,none,

lib/AST/Decl.cpp

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

4836-
// Bound generic types are invariant.
4837-
if (auto boundGenericType = type->getAs<BoundGenericType>()) {
4836+
// Most bound generic types are invariant.
4837+
if (auto *const bgt = type->getAs<BoundGenericType>()) {
48384838
auto info = SelfReferenceInfo();
4839-
for (auto paramType : boundGenericType->getGenericArgs()) {
4840-
info |= findProtocolSelfReferences(proto, paramType,
4839+
4840+
const auto &ctx = bgt->getDecl()->getASTContext();
4841+
if (ctx.getArrayDecl() == bgt->getDecl()) {
4842+
// Swift.Array preserves variance in its Value type.
4843+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
4844+
position);
4845+
} else if (bgt->getDecl() == ctx.getDictionaryDecl()) {
4846+
// Swift.Dictionary preserves variance in its Element type.
4847+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
48414848
SelfReferencePosition::Invariant);
4849+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
4850+
position);
4851+
} else {
4852+
for (auto paramType : bgt->getGenericArgs()) {
4853+
info |= findProtocolSelfReferences(proto, paramType,
4854+
SelfReferencePosition::Invariant);
4855+
}
48424856
}
48434857

48444858
return info;

lib/Sema/CSApply.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6785,6 +6785,19 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
67856785
return result;
67866786
}
67876787

6788+
case TypeKind::BoundGenericStruct: {
6789+
auto toStruct = cast<BoundGenericStructType>(desugaredToType);
6790+
if (toStruct->getDecl() != ctx.getArrayDecl() &&
6791+
toStruct->getDecl() != ctx.getDictionaryDecl())
6792+
break;
6793+
6794+
if (toStruct->getDecl() == cs.getType(expr)->getAnyNominal())
6795+
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
6796+
locator);
6797+
6798+
break;
6799+
}
6800+
67886801
#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
67896802
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
67906803
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
@@ -6798,7 +6811,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
67986811
case TypeKind::Struct:
67996812
case TypeKind::Class:
68006813
case TypeKind::BoundGenericClass:
6801-
case TypeKind::BoundGenericStruct:
68026814
case TypeKind::Metatype:
68036815
case TypeKind::DynamicSelf:
68046816
case TypeKind::PrimaryArchetype:

test/SILGen/collection_upcast.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,21 @@ func testSetUpcastBridged(_ set: Set<BridgedSwift>) {
108108
// CHECK-NOT: destroy_value [[SET]]
109109
let anyObjectSet = set as Set<NSObject>
110110
}
111+
112+
protocol P {
113+
func selfArrayUpcast() -> [Self]
114+
func selfDictionaryUpcast() -> [String : Self]
115+
}
116+
117+
// CHECK-LABEL: sil hidden [ossa] @$s17collection_upcast44testCollectionUpcastWithCovariantSelfErasure{{.*}}F
118+
// CHECK: bb0([[ARG:%0]] : $*P):
119+
func testCollectionUpcastWithCovariantSelfErasure(arg: P) {
120+
let array = arg.selfArrayUpcast() // [P]
121+
// CHECK: open_existential_addr immutable_access [[ARG]] : $*P to $*@opened([[N:".*"]]) P
122+
// CHECK: [[UPCAST_FN:%[0-9]+]] = function_ref @$ss15_arrayForceCast{{.*}}F
123+
// CHECK: apply [[UPCAST_FN]]<@opened([[N]]) P, P>(%{{[0-9]+}}) : $@convention(thin) <τ_0_0, τ_0_1> (@guaranteed Array<τ_0_0>) -> @owned Array<τ_0_1>
124+
let dictionary = arg.selfDictionaryUpcast() // [String : P]
125+
// CHECK: open_existential_addr immutable_access [[ARG]] : $*P to $*@opened([[N:".*"]]) P
126+
// CHECK: [[UPCAST_FN:%[0-9]+]] = function_ref @$ss17_dictionaryUpCast{{.*}}F
127+
// CHECK: apply [[UPCAST_FN]]<String, @opened([[N]]) P, String, P>(%{{[0-9]+}}) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@guaranteed Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
128+
}

test/decl/ext/generic.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ extension Array where Element == String { }
149149
extension GenericClass : P3 where T : P3 { }
150150

151151
extension GenericClass where Self : P3 { }
152-
// expected-error@-1{{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'GenericClass'?}} {{30-34=GenericClass}}
152+
// expected-error@-1{{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'GenericClass'?}} {{30-34=GenericClass}}
153153
// expected-error@-2{{'GenericClass<T>' in conformance requirement does not refer to a generic parameter or associated type}}
154154

155155
protocol P4 {

test/decl/func/dynamic_self.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ enum E0 {
2424
class C0 {
2525
func f() -> Self { } // okay
2626

27-
func g(_ ds: Self) { } // expected-error{{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'C0'?}}
27+
func g(_ ds: Self) { } // expected-error{{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'C0'?}}
2828

29-
func h(_ ds: Self) -> Self { } // expected-error{{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'C0'?}}
29+
func h(_ ds: Self) -> Self { } // expected-error{{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'C0'?}}
3030
}
3131

3232
protocol P0 {

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+
}

test/type/self.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ struct S0<T> {
55
}
66

77
class C0<T> {
8-
func foo(_ other: Self) { } // expected-error{{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'C0'?}}
8+
func foo(_ other: Self) { } // expected-error{{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'C0'?}}
99
}
1010

1111
enum E0<T> {
@@ -47,15 +47,15 @@ final class FinalMario : Mario {
4747

4848
class A<T> {
4949
typealias _Self = Self
50-
// expected-error@-1 {{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'A'?}}
50+
// expected-error@-1 {{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'A'?}}
5151
let b: Int
5252
required init(a: Int) {
5353
print("\(Self.self).\(#function)")
5454
Self.y()
5555
b = a
5656
}
5757
static func z(n: Self? = nil) {
58-
// expected-error@-1 {{covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'A'?}}
58+
// expected-error@-1 {{covariant 'Self' can only appear as the possibly optional type of a property, subscript or method result; did you mean 'A'?}}
5959
print("\(Self.self).\(#function)")
6060
}
6161
class func y() {

0 commit comments

Comments
 (0)