Skip to content

Commit edf98aa

Browse files
authored
Merge pull request #41054 from tshortli/check-protocol-conformance-availability-for-members
[Sema] Check protocol conformance availability for member reference and type erasing expressions
2 parents 23557b8 + d575bba commit edf98aa

File tree

4 files changed

+122
-14
lines changed

4 files changed

+122
-14
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ _**Note:** This is in reverse chronological order, so newer entries are added to
55

66
## Swift 5.7
77

8+
* The compiler now correctly emits warnings for more kinds of expressions where a protocol conformance is used and may be unavailable at runtime. Previously, member reference expressions and type erasing expressions that used potentially unavailable conformances were not diagnosed, leading to potential crashes at runtime.
9+
10+
```swift
11+
struct Pancake {}
12+
protocol Food {}
13+
14+
extension Food {
15+
var isGlutenFree: Bool { false }
16+
}
17+
18+
@available(macOS 12.0, *)
19+
extension Pancake: Food {}
20+
21+
@available(macOS 11.0, *)
22+
func eatPancake(_ pancake: Pancake) {
23+
if (pancake.isGlutenFree) { // warning: conformance of 'Pancake' to 'Food' is only available in macOS 12.0 or newer
24+
eatFood(pancake) // warning: conformance of 'Pancake' to 'Food' is only available in macOS 12.0 or newer
25+
}
26+
}
27+
28+
func eatFood(_ food: Food) {}
29+
```
30+
831
* [SE-0328][]:
932

1033
Opaque types (expressed with 'some') can now be used in structural positions

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,12 @@ class ExprAvailabilityWalker : public ASTWalker {
28632863
return skipChildren();
28642864
}
28652865
}
2866+
2867+
if (auto EE = dyn_cast<ErasureExpr>(E)) {
2868+
for (ProtocolConformanceRef C : EE->getConformances()) {
2869+
diagnoseConformanceAvailability(E->getLoc(), C, Where);
2870+
}
2871+
}
28662872

28672873
return visitChildren();
28682874
}
@@ -2950,15 +2956,15 @@ class ExprAvailabilityWalker : public ASTWalker {
29502956
// writeback. The AST should have this information.
29512957
walkInContext(E, E->getBase(), MemberAccessContext::Getter);
29522958

2953-
ValueDecl *D = E->getMember().getDecl();
2959+
ConcreteDeclRef DR = E->getMember();
29542960
// Diagnose for the member declaration itself.
2955-
if (diagnoseDeclAvailability(D, E->getNameLoc().getSourceRange(),
2956-
nullptr, Where))
2961+
if (diagnoseDeclRefAvailability(DR, E->getNameLoc().getSourceRange(),
2962+
getEnclosingApplyExpr(), None))
29572963
return;
29582964

29592965
// Diagnose for appropriate accessors, given the access context.
29602966
auto *DC = Where.getDeclContext();
2961-
maybeDiagStorageAccess(D, E->getSourceRange(), DC);
2967+
maybeDiagStorageAccess(DR.getDecl(), E->getSourceRange(), DC);
29622968
}
29632969

29642970
/// Walk a keypath expression, checking all of its components for
@@ -3099,7 +3105,8 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30993105
return true;
31003106
}
31013107

3102-
diagnoseDeclAvailability(D, R, call, Where, Flags);
3108+
if (diagnoseDeclAvailability(D, R, call, Where, Flags))
3109+
return true;
31033110

31043111
if (R.isValid()) {
31053112
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),

test/Sema/conformance_availability.swift

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
public protocol Horse {}
66
func takesHorse<T : Horse>(_: T) {}
7+
func takesHorseExistential(_: Horse) {}
78

89
extension Horse {
910
func giddyUp() {}
11+
var isGalloping: Bool { true }
1012
}
1113

1214
struct UsesHorse<T : Horse> {}
@@ -16,18 +18,24 @@ public struct HasUnavailableConformance1 {}
1618

1719
@available(*, unavailable)
1820
extension HasUnavailableConformance1 : Horse {}
19-
// expected-note@-1 4{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
21+
// expected-note@-1 7{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
2022

2123
func passUnavailableConformance1(x: HasUnavailableConformance1) {
2224
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
25+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
2326
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
27+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
28+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
2429
_ = UsesHorse<HasUnavailableConformance1>.self // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
2530
}
2631

2732
@available(*, unavailable)
2833
func passUnavailableConformance1a(x: HasUnavailableConformance1) {
2934
takesHorse(x)
35+
takesHorseExistential(x)
3036
x.giddyUp()
37+
_ = x.isGalloping
38+
_ = x[keyPath: \.isGalloping]
3139
_ = UsesHorse<HasUnavailableConformance1>.self
3240
}
3341

@@ -36,38 +44,50 @@ public struct HasUnavailableConformance2 {}
3644

3745
@available(macOS, unavailable)
3846
extension HasUnavailableConformance2 : Horse {}
39-
// expected-note@-1 3{{conformance of 'HasUnavailableConformance2' to 'Horse' has been explicitly marked unavailable here}}
47+
// expected-note@-1 6{{conformance of 'HasUnavailableConformance2' to 'Horse' has been explicitly marked unavailable here}}
4048

4149
func passUnavailableConformance2(x: HasUnavailableConformance2) {
4250
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
51+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
4352
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
53+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
54+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
4455
_ = UsesHorse<HasUnavailableConformance2>.self // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
4556
}
4657

4758
@available(macOS, unavailable)
4859
func passUnavailableConformance2a(x: HasUnavailableConformance2) {
4960
// This is allowed
5061
takesHorse(x)
62+
takesHorseExistential(x)
5163
x.giddyUp()
64+
_ = x.isGalloping
65+
_ = x[keyPath: \.isGalloping]
5266
}
5367

5468
// Swift version unavailability
5569
public struct HasUnavailableConformance3 {}
5670

5771
@available(swift 12)
5872
extension HasUnavailableConformance3 : Horse {}
59-
// expected-note@-1 6{{conformance of 'HasUnavailableConformance3' to 'Horse' was introduced in Swift 12}}
73+
// expected-note@-1 12{{conformance of 'HasUnavailableConformance3' to 'Horse' was introduced in Swift 12}}
6074

6175
func passUnavailableConformance3(x: HasUnavailableConformance3) {
6276
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
77+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
6378
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
79+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
80+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
6481
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
6582
}
6683

6784
@available(swift 12)
6885
func passUnavailableConformance3a(x: HasUnavailableConformance3) {
6986
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
87+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
7088
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
89+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
90+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
7191
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
7292
}
7393

@@ -76,18 +96,24 @@ public struct HasUnavailableConformance4 {}
7696

7797
@available(macOS, obsoleted: 10.1)
7898
extension HasUnavailableConformance4 : Horse {}
79-
// expected-note@-1 6{{conformance of 'HasUnavailableConformance4' to 'Horse' was obsoleted in macOS 10.1}}
99+
// expected-note@-1 12{{conformance of 'HasUnavailableConformance4' to 'Horse' was obsoleted in macOS 10.1}}
80100

81101
func passUnavailableConformance4(x: HasUnavailableConformance4) {
82102
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
103+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
83104
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
105+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable}}
106+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable}}
84107
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
85108
}
86109

87110
@available(macOS, obsoleted: 10.1)
88111
func passUnavailableConformance4a(x: HasUnavailableConformance4) {
89112
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
113+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
90114
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
115+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable}}
116+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable}}
91117
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
92118
}
93119

@@ -96,18 +122,24 @@ public struct HasUnavailableConformance5 {}
96122

97123
@available(swift, obsoleted: 4)
98124
extension HasUnavailableConformance5 : Horse {}
99-
// expected-note@-1 6{{conformance of 'HasUnavailableConformance5' to 'Horse' was obsoleted in Swift 4}}
125+
// expected-note@-1 12{{conformance of 'HasUnavailableConformance5' to 'Horse' was obsoleted in Swift 4}}
100126

101127
func passUnavailableConformance5(x: HasUnavailableConformance5) {
102128
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
129+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
103130
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
131+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
132+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
104133
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
105134
}
106135

107136
@available(swift, obsoleted: 4)
108137
func passUnavailableConformance5a(x: HasUnavailableConformance5) {
109138
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
139+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
110140
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
141+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
142+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
111143
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
112144
}
113145

@@ -116,11 +148,14 @@ public struct HasUnavailableConformance6 {}
116148

117149
@available(*, unavailable, message: "This conformance is bad")
118150
extension HasUnavailableConformance6 : Horse {}
119-
// expected-note@-1 3{{conformance of 'HasUnavailableConformance6' to 'Horse' has been explicitly marked unavailable here}}
151+
// expected-note@-1 6{{conformance of 'HasUnavailableConformance6' to 'Horse' has been explicitly marked unavailable here}}
120152

121153
func passUnavailableConformance6(x: HasUnavailableConformance6) {
122154
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
155+
takesHorseExistential(x) // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
123156
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
157+
_ = x.isGalloping // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
158+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
124159
_ = UsesHorse<HasUnavailableConformance6>.self // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
125160
}
126161

@@ -132,14 +167,20 @@ extension HasDeprecatedConformance1 : Horse {}
132167

133168
func passDeprecatedConformance1(x: HasDeprecatedConformance1) {
134169
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
170+
takesHorseExistential(x) // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
135171
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
172+
_ = x.isGalloping // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
173+
_ = x[keyPath: \.isGalloping] // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
136174
_ = UsesHorse<HasDeprecatedConformance1>.self // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
137175
}
138176

139177
@available(*, deprecated)
140178
func passDeprecatedConformance1a(x: HasDeprecatedConformance1) {
141179
takesHorse(x)
180+
takesHorseExistential(x)
142181
x.giddyUp()
182+
_ = x.isGalloping
183+
_ = x[keyPath: \.isGalloping]
143184
_ = UsesHorse<HasDeprecatedConformance1>.self
144185
}
145186

@@ -151,14 +192,20 @@ extension HasDeprecatedConformance2 : Horse {}
151192

152193
func passDeprecatedConformance2(x: HasDeprecatedConformance2) {
153194
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
195+
takesHorseExistential(x) // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
154196
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
197+
_ = x.isGalloping // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
198+
_ = x[keyPath: \.isGalloping] // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
155199
_ = UsesHorse<HasDeprecatedConformance2>.self // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
156200
}
157201

158202
@available(*, deprecated)
159203
func passDeprecatedConformance2a(x: HasDeprecatedConformance2) {
160204
takesHorse(x)
205+
takesHorseExistential(x)
161206
x.giddyUp()
207+
_ = x.isGalloping
208+
_ = x[keyPath: \.isGalloping]
162209
_ = UsesHorse<HasDeprecatedConformance2>.self
163210
}
164211

@@ -170,7 +217,10 @@ extension HasDeprecatedConformance3 : Horse {}
170217

171218
func passDeprecatedConformance3(x: HasDeprecatedConformance3) {
172219
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
220+
takesHorseExistential(x) // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
173221
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
222+
_ = x.isGalloping // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
223+
_ = x[keyPath: \.isGalloping] // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
174224
_ = UsesHorse<HasDeprecatedConformance3>.self // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
175225
}
176226

@@ -180,7 +230,10 @@ func passDeprecatedConformance3a(x: HasDeprecatedConformance3) {
180230
// This branch is dead with our minimum deployment target, so don't emit
181231
// deprecation diagnostics in it.
182232
takesHorse(x)
233+
takesHorseExistential(x)
183234
x.giddyUp()
235+
_ = x.isGalloping
236+
_ = x[keyPath: \.isGalloping]
184237
_ = UsesHorse<HasDeprecatedConformance3>.self
185238
}
186239
}
@@ -196,21 +249,33 @@ extension HasAvailableConformance1 : Horse {}
196249
// in test/Sema/conformance_availability_warn.swift for the same example
197250
// but without this flag.
198251

199-
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 3{{add @available attribute to enclosing global function}}
252+
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 6{{add @available attribute to enclosing global function}}
200253
takesHorse(x) // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
201254
// expected-note@-1 {{add 'if #available' version check}}
202255

256+
takesHorseExistential(x) // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
257+
// expected-note@-1 {{add 'if #available' version check}}
258+
203259
x.giddyUp() // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
204260
// expected-note@-1 {{add 'if #available' version check}}
205261

262+
_ = x.isGalloping // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
263+
// expected-note@-1 {{add 'if #available' version check}}
264+
265+
_ = x[keyPath: \.isGalloping] // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
266+
// expected-note@-1 {{add 'if #available' version check}}
267+
206268
_ = UsesHorse<HasAvailableConformance1>.self // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
207269
// expected-note@-1 {{add 'if #available' version check}}
208270
}
209271

210272
@available(macOS 100, *)
211273
func passAvailableConformance1a(x: HasAvailableConformance1) {
212274
takesHorse(x)
275+
takesHorseExistential(x)
213276
x.giddyUp()
277+
_ = x.isGalloping
278+
_ = x[keyPath: \.isGalloping]
214279
_ = UsesHorse<HasAvailableConformance1>.self
215280
}
216281

0 commit comments

Comments
 (0)