Skip to content

Commit bfe3fe7

Browse files
authored
Merge pull request #80707 from tshortli/unavailable-and-introduced-6.2
[6.2] AST: Stop diagnosing potentially unavailable declarations in unavailable contexts
2 parents 097dba3 + 64fadaf commit bfe3fe7

8 files changed

+204
-76
lines changed

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@
55
66
## Swift 6.2
77

8+
* The Swift compiler no longer diagnoses references to declarations that are
9+
potentially unavailable because the platform version might not be new enough
10+
when those references occur inside of contexts that are also unavailable to
11+
that platform. This addresses a long-standing nuisance for multi-platform
12+
code. However, there is also a chance that existing source code may become
13+
ambiguous as a result:
14+
15+
```swift
16+
struct A {}
17+
struct B {}
18+
19+
func potentiallyAmbiguous(_: A) {}
20+
21+
@available(macOS 99, *)
22+
func potentiallyAmbiguous(_: B) {}
23+
24+
@available(macOS, unavailable)
25+
func unavailableOnMacOS() {
26+
potentiallyAmbiguous(.init()) // error: ambiguous use of 'init()'
27+
}
28+
```
29+
30+
Code that is now ambiguous as a result should likely be restructured since
31+
disambiguation based on platform introduction alone has never been a reliable
32+
strategy, given that the code would eventually become ambiguous anyways when
33+
the deployment target is raised.
34+
835
* [SE-0419][]:
936
Introduced the new `Runtime` module, which contains a public API that can
1037
generate backtraces, presently supported on macOS and Linux. Capturing a

lib/AST/AvailabilityConstraint.cpp

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,25 +113,59 @@ DeclAvailabilityConstraints::getPrimaryConstraint() const {
113113
return result;
114114
}
115115

116+
static bool canIgnoreConstraintInUnavailableContexts(
117+
const Decl *decl, const AvailabilityConstraint &constraint) {
118+
auto domain = constraint.getDomain();
119+
120+
switch (constraint.getReason()) {
121+
case AvailabilityConstraint::Reason::UnconditionallyUnavailable:
122+
// Always reject uses of universally unavailable declarations, regardless
123+
// of context, since there are no possible compilation configurations in
124+
// which they are available. However, make an exception for types and
125+
// conformances, which can sometimes be awkward to avoid references to.
126+
if (!isa<TypeDecl>(decl) && !isa<ExtensionDecl>(decl)) {
127+
if (domain.isUniversal() || domain.isSwiftLanguage())
128+
return false;
129+
}
130+
return true;
131+
132+
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
133+
switch (domain.getKind()) {
134+
case AvailabilityDomain::Kind::Universal:
135+
case AvailabilityDomain::Kind::SwiftLanguage:
136+
case AvailabilityDomain::Kind::PackageDescription:
137+
case AvailabilityDomain::Kind::Embedded:
138+
case AvailabilityDomain::Kind::Custom:
139+
return false;
140+
case AvailabilityDomain::Kind::Platform:
141+
// Platform availability only applies to the target triple that the
142+
// binary is being compiled for. Since the same declaration can be
143+
// potentially unavailable from a given context when compiling for one
144+
// platform, but available from that context when compiling for a
145+
// different platform, it is overly strict to enforce potential platform
146+
// unavailability constraints in contexts that are unavailable to that
147+
// platform.
148+
return true;
149+
}
150+
return constraint.getDomain().isPlatform();
151+
152+
case AvailabilityConstraint::Reason::Obsoleted:
153+
case AvailabilityConstraint::Reason::UnavailableForDeployment:
154+
return false;
155+
}
156+
}
157+
116158
static bool
117-
isInsideCompatibleUnavailableDeclaration(const Decl *decl,
118-
const SemanticAvailableAttr &attr,
119-
const AvailabilityContext &context) {
159+
shouldIgnoreConstraintInContext(const Decl *decl,
160+
const AvailabilityConstraint &constraint,
161+
const AvailabilityContext &context) {
120162
if (!context.isUnavailable())
121163
return false;
122164

123-
if (!attr.isUnconditionallyUnavailable())
165+
if (!canIgnoreConstraintInUnavailableContexts(decl, constraint))
124166
return false;
125167

126-
// Refuse calling universally unavailable functions from unavailable code,
127-
// but allow the use of types.
128-
auto domain = attr.getDomain();
129-
if (!isa<TypeDecl>(decl) && !isa<ExtensionDecl>(decl)) {
130-
if (domain.isUniversal() || domain.isSwiftLanguage())
131-
return false;
132-
}
133-
134-
return context.containsUnavailableDomain(domain);
168+
return context.containsUnavailableDomain(constraint.getDomain());
135169
}
136170

137171
/// Returns the `AvailabilityConstraint` that describes how \p attr restricts
@@ -218,8 +252,7 @@ static void getAvailabilityConstraintsForDecl(
218252
// declaration is unconditionally unavailable in a domain for which
219253
// the context is already unavailable.
220254
llvm::erase_if(constraints, [&](const AvailabilityConstraint &constraint) {
221-
return isInsideCompatibleUnavailableDeclaration(decl, constraint.getAttr(),
222-
context);
255+
return shouldIgnoreConstraintInContext(decl, constraint, context);
223256
});
224257
}
225258

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// REQUIRES: OS=macosx
4+
5+
struct A {} // expected-note * {{found this candidate}}
6+
struct B {} // expected-note * {{found this candidate}}
7+
8+
func ambiguousInFarFuture(_: A) {}
9+
10+
@available(macOS 99, *)
11+
func ambiguousInFarFuture(_: B) {}
12+
13+
struct S {
14+
func ambiguousInFarFuture(_: A) {}
15+
}
16+
17+
@available(macOS 99, *)
18+
extension S {
19+
func ambiguousInFarFuture(_: B) {}
20+
}
21+
22+
func testDeploymentTarget(_ s: S) {
23+
ambiguousInFarFuture(.init())
24+
s.ambiguousInFarFuture(.init())
25+
}
26+
27+
@available(macOS 99, *)
28+
func testFarFuture(_ s: S) {
29+
ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}}
30+
s.ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}}
31+
}
32+
33+
@available(macOS, unavailable)
34+
func testUnavailable(_ s: S) {
35+
ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}}
36+
s.ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}}
37+
}

test/Sema/availability_scopes.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,19 +253,19 @@ extension SomeClass {
253253

254254
// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass
255255
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeClass
256-
// CHECK-NEXT: {{^}} (decl version=51 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt()
256+
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt()
257257
// CHECK-NEXT: {{^}} (condition_following_availability version=52 unavailable=macOS
258258
// CHECK-NEXT: {{^}} (condition_following_availability version=53 unavailable=macOS
259259
// CHECK-NEXT: {{^}} (if_then version=53 unavailable=macOS
260260
// CHECK-NEXT: {{^}} (condition_following_availability version=54 unavailable=macOS
261261
// CHECK-NEXT: {{^}} (if_then version=54 unavailable=macOS
262262
// CHECK-NEXT: {{^}} (condition_following_availability version=55 unavailable=macOS
263-
// CHECK-NEXT: {{^}} (decl version=55 unavailable=macOS decl=funcInGuardElse()
263+
// CHECK-NEXT: {{^}} (decl version=54 unavailable=macOS decl=funcInGuardElse()
264264
// CHECK-NEXT: {{^}} (guard_fallthrough version=55 unavailable=macOS
265265
// CHECK-NEXT: {{^}} (condition_following_availability version=56 unavailable=macOS
266266
// CHECK-NEXT: {{^}} (guard_fallthrough version=56 unavailable=macOS
267-
// CHECK-NEXT: {{^}} (decl version=57 unavailable=macOS decl=funcInInnerIfElse()
268-
// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInOuterIfElse()
267+
// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInInnerIfElse()
268+
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=funcInOuterIfElse()
269269
@available(OSX, unavailable)
270270
extension SomeClass {
271271
@available(OSX 51, *)
@@ -401,7 +401,7 @@ extension SomeEnum {
401401
// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum
402402
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeEnum
403403
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=availableMacOS_52
404-
// CHECK-NEXT: {{^}} (decl version=52 unavailable=macOS decl=availableMacOS_52
404+
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=availableMacOS_52
405405
// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=neverAvailable()
406406

407407
@available(macOS, unavailable)
@@ -418,10 +418,25 @@ extension SomeEnum {
418418

419419
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroduced()
420420

421-
@available(macOS, unavailable, introduced: 52)
421+
@available(macOS, unavailable)
422+
@available(macOS, introduced: 52)
422423
func unavailableOnMacOSAndIntroduced() {
423424
}
424425

426+
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=introducedOnMacOSAndUnavailable()
427+
428+
@available(macOS, introduced: 53)
429+
@available(macOS, unavailable)
430+
func introducedOnMacOSAndUnavailable() {
431+
}
432+
433+
434+
// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroducedSameAttr()
435+
436+
@available(macOS, unavailable, introduced: 54)
437+
func unavailableOnMacOSAndIntroducedSameAttr() {
438+
}
439+
425440
// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=NeverAvailable
426441
// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=unavailableOnMacOS()
427442

test/Sema/property_wrapper_availability.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ struct UnavailableStruct {
9999
@UnavailableWrapper var unavailableInferred = S()
100100

101101
@WrappedValueUnavailableOnMacOS var unavailableWrappedValue: S
102-
@WrappedValueAvailable51 var wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
102+
@WrappedValueAvailable51 var wrappedValueAavailable51: S
103103
}
104104

105105
@available(macOS, unavailable)
@@ -117,7 +117,7 @@ struct UnavailableOnMacOSStruct {
117117
@UnavailableWrapper var unavailableInferred = S()
118118

119119
@WrappedValueUnavailableOnMacOS var unavailableWrappedValue: S
120-
@WrappedValueAvailable51 var wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
120+
@WrappedValueAvailable51 var wrappedValueAavailable51: S
121121
}
122122

123123
func alwaysAvailableFunc( // expected-note 4 {{add @available attribute to enclosing global function}}
@@ -160,14 +160,14 @@ func unavailableFunc(
160160
@DeprecatedWrapper _ deprecated: S,
161161
@UnavailableWrapper _ unavailable: S,
162162
@WrappedValueUnavailableOnMacOS _ unavailableWrappedValue: S,
163-
@WrappedValueAvailable51 _ wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
163+
@WrappedValueAvailable51 _ wrappedValueAavailable51: S
164164
) {
165165
@AlwaysAvailableWrapper var alwaysAvailableLocal = S()
166166
@Available51Wrapper var available51Local = S()
167167
@DeprecatedWrapper var deprecatedLocal = S()
168168
@UnavailableWrapper var unavailableLocal = S()
169169
@WrappedValueUnavailableOnMacOS var unavailableWrappedValueLocal = S()
170-
@WrappedValueAvailable51 var wrappedValueAavailable51 = S() // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
170+
@WrappedValueAvailable51 var wrappedValueAavailable51 = S()
171171
}
172172

173173
@available(macOS, unavailable)
@@ -177,12 +177,12 @@ func unavailableOnMacOSFunc(
177177
@DeprecatedWrapper _ deprecated: S,
178178
@UnavailableWrapper _ unavailable: S,
179179
@WrappedValueUnavailableOnMacOS _ unavailableWrappedValue: S,
180-
@WrappedValueAvailable51 _ wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
180+
@WrappedValueAvailable51 _ wrappedValueAavailable51: S
181181
) {
182182
@AlwaysAvailableWrapper var alwaysAvailableLocal = S()
183183
@Available51Wrapper var available51Local = S()
184184
@DeprecatedWrapper var deprecatedLocal = S()
185185
@UnavailableWrapper var unavailableLocal = S()
186186
@WrappedValueUnavailableOnMacOS var unavailableWrappedValueLocal = S()
187-
@WrappedValueAvailable51 var wrappedValueAavailable51 = S() // expected-error {{'wrappedValue' is only available in macOS 51 or newer}}
187+
@WrappedValueAvailable51 var wrappedValueAavailable51 = S()
188188
}

0 commit comments

Comments
 (0)