Skip to content

Commit 791e208

Browse files
committed
AST: Improved inferred availability accuracy.
Previously, when creating availability attributes for synthesized declarations we would identify some set of reference declarations that the synthesized declaration should be as-available-as. The availability attributes of the reference declarations would then be merged together to create the attributes for the synthesized declaration. The problem with this approach is that the reference declarations themselves may implicitly inherit availability from their enclosing scopes, so the resulting merged attributes could be incomplete. The fix is to walk though the enclosing scopes of the reference declarations, merging the availability attributes found at each level. Additionally, the merging algorithm that produces inferred availability attributes failed to deal with conflicts between platform specific and platform agnostic availability. Now the merging algorithm notices when platform agnostic availability should take precedence and avoids generating conflicting platform specific attributes. Resolves rdar://106575142
1 parent 4ff7fbe commit 791e208

File tree

4 files changed

+147
-11
lines changed

4 files changed

+147
-11
lines changed

lib/AST/Availability.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -143,26 +143,54 @@ void AvailabilityInference::applyInferredAvailableAttrs(
143143
// a per-platform basis.
144144
std::map<PlatformKind, InferredAvailability> Inferred;
145145
for (const Decl *D : InferredFromDecls) {
146-
for (const DeclAttribute *Attr : D->getAttrs()) {
147-
auto *AvAttr = dyn_cast<AvailableAttr>(Attr);
148-
if (!AvAttr || AvAttr->isInvalid())
149-
continue;
146+
do {
147+
for (const DeclAttribute *Attr : D->getAttrs()) {
148+
auto *AvAttr = dyn_cast<AvailableAttr>(Attr);
149+
if (!AvAttr || AvAttr->isInvalid())
150+
continue;
150151

151-
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr->Platform]);
152+
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr->Platform]);
152153

153-
if (Message.empty() && !AvAttr->Message.empty())
154-
Message = AvAttr->Message;
154+
if (Message.empty() && !AvAttr->Message.empty())
155+
Message = AvAttr->Message;
155156

156-
if (Rename.empty() && !AvAttr->Rename.empty()) {
157-
Rename = AvAttr->Rename;
158-
RenameDecl = AvAttr->RenameDecl;
157+
if (Rename.empty() && !AvAttr->Rename.empty()) {
158+
Rename = AvAttr->Rename;
159+
RenameDecl = AvAttr->RenameDecl;
160+
}
159161
}
162+
163+
// Walk up the enclosing declaration hierarchy to make sure we aren't
164+
// missing any inherited attributes.
165+
D = AvailabilityInference::parentDeclForInferredAvailability(D);
166+
} while (D);
167+
}
168+
169+
DeclAttributes &Attrs = ToDecl->getAttrs();
170+
171+
// Some kinds of platform agnostic availability supersede any platform
172+
// specific availability.
173+
auto InferredAgnostic = Inferred.find(PlatformKind::none);
174+
if (InferredAgnostic != Inferred.end()) {
175+
switch (InferredAgnostic->second.PlatformAgnostic) {
176+
case PlatformAgnosticAvailabilityKind::Deprecated:
177+
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
178+
case PlatformAgnosticAvailabilityKind::Unavailable:
179+
Attrs.add(createAvailableAttr(PlatformKind::none,
180+
InferredAgnostic->second, Message, Rename,
181+
RenameDecl, Context));
182+
return;
183+
184+
case PlatformAgnosticAvailabilityKind::None:
185+
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
186+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
187+
case PlatformAgnosticAvailabilityKind::NoAsync:
188+
break;
160189
}
161190
}
162191

163192
// Create an availability attribute for each observed platform and add
164193
// to ToDecl.
165-
DeclAttributes &Attrs = ToDecl->getAttrs();
166194
for (auto &Pair : Inferred) {
167195
auto *Attr = createAvailableAttr(Pair.first, Pair.second, Message,
168196
Rename, RenameDecl, Context);

test/ModuleInterface/actor_availability.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,16 @@ extension Enum {
6565
// CHECK-NEXT: get
6666
// CHECK-NEXT: }
6767
}
68+
69+
// CHECK: #if compiler(>=5.3) && $Actors
70+
// CHECK-NEXT: @_hasMissingDesignatedInitializers @available(macOS, unavailable)
71+
// CHECK-NEXT: public actor UnavailableExtensionNestedActor {
72+
@available(macOS, unavailable)
73+
public actor UnavailableExtensionNestedActor {
74+
// CHECK: @available(iOS 13.4, tvOS 13.4, watchOS 6.2, *)
75+
// CHECK-NEXT: @available(macOS, unavailable)
76+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
77+
// CHECK-NEXT: get
78+
// CHECK-NEXT: }
79+
}
6880
}

test/Sema/generalized_accessors_availability.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,40 @@ struct ModifyConditionallyAvailable<T> {
3737
}
3838
}
3939

40+
@propertyWrapper
41+
struct SetterMoreAvailable<T> {
42+
var wrappedValue: T {
43+
get { fatalError() }
44+
45+
@available(macOS 10.49, *)
46+
set { fatalError() }
47+
}
48+
49+
var projectedValue: T {
50+
get { fatalError() }
51+
52+
@available(macOS 10.49, *)
53+
set { fatalError() }
54+
}
55+
}
56+
57+
@propertyWrapper
58+
struct ModifyMoreAvailable<T> {
59+
var wrappedValue: T {
60+
get { fatalError() }
61+
62+
@available(macOS 10.49, *)
63+
_modify { fatalError() }
64+
}
65+
66+
var projectedValue: T {
67+
get { fatalError() }
68+
69+
@available(macOS 10.49, *)
70+
_modify { fatalError() }
71+
}
72+
}
73+
4074
struct Butt {
4175
var modify_conditionally_available: Int {
4276
get { fatalError() }
@@ -50,6 +84,12 @@ struct Butt {
5084

5185
@ModifyConditionallyAvailable
5286
var wrapped_modify_conditionally_available: Int
87+
88+
@SetterMoreAvailable
89+
var wrapped_setter_more_available: Int
90+
91+
@ModifyMoreAvailable
92+
var wrapped_modify_more_available: Int
5393
}
5494

5595
func butt(x: inout Butt) { // expected-note * {{}}
@@ -58,6 +98,10 @@ func butt(x: inout Butt) { // expected-note * {{}}
5898
x.wrapped_modify_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
5999
x.$wrapped_setter_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
60100
x.$wrapped_modify_conditionally_available = 0 // expected-error {{only available in macOS 10.51 or newer}} expected-note{{}}
101+
x.wrapped_setter_more_available = 0
102+
x.wrapped_modify_more_available = 0
103+
x.$wrapped_setter_more_available = 0
104+
x.$wrapped_modify_more_available = 0
61105

62106
if #available(macOS 10.51, *) {
63107
x.modify_conditionally_available = 0
@@ -68,6 +112,25 @@ func butt(x: inout Butt) { // expected-note * {{}}
68112
}
69113
}
70114

115+
@available(macOS, unavailable)
116+
extension Butt {
117+
@available(iOS, unavailable)
118+
struct Nested { // expected-note {{has been explicitly marked unavailable here}}
119+
@SetterMoreAvailable
120+
var wrapped_setter_more_available: Int // expected-note 2 {{has been explicitly marked unavailable here}}
121+
122+
@ModifyMoreAvailable
123+
var wrapped_modify_more_available: Int // expected-note 2 {{has been explicitly marked unavailable here}}
124+
}
125+
}
126+
127+
func testButtNested(x: inout Butt.Nested) { // expected-error {{'Nested' is unavailable in macOS}}
128+
x.wrapped_setter_more_available = 0 // expected-error {{is unavailable in macOS}}
129+
x.wrapped_modify_more_available = 0 // expected-error {{is unavailable in macOS}}
130+
x.$wrapped_setter_more_available = 0 // expected-error {{is unavailable in macOS}}
131+
x.$wrapped_modify_more_available = 0 // expected-error {{is unavailable in macOS}}
132+
}
133+
71134
@available(macOS 11.0, *)
72135
struct LessAvailable {
73136
@SetterConditionallyAvailable
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx12 -swift-version 5
2+
3+
// REQUIRES: objc_interop
4+
// REQUIRES: OS=macosx
5+
6+
import SwiftUI
7+
8+
@available(macOS 12, *)
9+
struct S {}
10+
11+
@available(iOS, unavailable)
12+
extension S {
13+
class NestedOtherPlatformUnavailable {
14+
@Published var x: Int = 0
15+
@Binding var y: Bool
16+
17+
init(y: Binding<Bool>) {
18+
_y = y
19+
}
20+
}
21+
}
22+
23+
@available(*, unavailable)
24+
extension S {
25+
class NestedAlwaysUnavailable {
26+
@Published var x: Int = 0
27+
@Binding var y: Bool
28+
29+
init(y: Binding<Bool>) {
30+
_y = y
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)