Skip to content

Commit 2996e93

Browse files
authored
Merge pull request #31025 from brentdax/dangerous-intersection
Fix @available(macCatalyst N, iOS N+M) bug
2 parents 0a037e7 + 05736d1 commit 2996e93

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

lib/AST/Availability.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,29 @@ void AvailabilityInference::applyInferredAvailableAttrs(
132132
}
133133
}
134134

135+
/// Returns true if the introduced version in \p newAttr should be used instead
136+
/// of the introduced version in \p prevAttr when both are attached to the same
137+
/// declaration and refer to the active platform.
138+
static bool isBetterThan(const AvailableAttr *newAttr,
139+
const AvailableAttr *prevAttr) {
140+
assert(newAttr);
141+
142+
// If there is no prevAttr, newAttr of course wins.
143+
if (!prevAttr)
144+
return true;
145+
146+
// If they belong to the same platform, the one that introduces later wins.
147+
if (prevAttr->Platform == newAttr->Platform)
148+
return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue();
149+
150+
// If the new attribute's platform inherits from the old one, it wins.
151+
return inheritsAvailabilityFromPlatform(newAttr->Platform,
152+
prevAttr->Platform);
153+
}
154+
135155
Optional<AvailabilityContext>
136156
AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
137-
Optional<AvailabilityContext> AnnotatedRange;
157+
const AvailableAttr *bestAvailAttr = nullptr;
138158

139159
for (auto Attr : D->getAttrs()) {
140160
auto *AvailAttr = dyn_cast<AvailableAttr>(Attr);
@@ -145,21 +165,15 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
145165
continue;
146166
}
147167

148-
AvailabilityContext AttrRange{
149-
VersionRange::allGTE(AvailAttr->Introduced.getValue())};
150-
151-
// If we have multiple introduction versions, we will conservatively
152-
// assume the worst case scenario. We may want to be more precise here
153-
// in the future or emit a diagnostic.
154-
155-
if (AnnotatedRange.hasValue()) {
156-
AnnotatedRange.getValue().intersectWith(AttrRange);
157-
} else {
158-
AnnotatedRange = AttrRange;
159-
}
168+
if (isBetterThan(AvailAttr, bestAvailAttr))
169+
bestAvailAttr = AvailAttr;
160170
}
161171

162-
return AnnotatedRange;
172+
if (!bestAvailAttr)
173+
return None;
174+
175+
return AvailabilityContext{
176+
VersionRange::allGTE(bestAvailAttr->Introduced.getValue())};
163177
}
164178

165179
AvailabilityContext AvailabilityInference::availableRange(const Decl *D,

test/attr/attr_availability_maccatalyst.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,51 @@ deprecatedOnIOSButNotMacCatalyst() // no-warning
4848
func introducedLaterOnMacCatalyst() {
4949
}
5050

51+
@available(iOS 57.0, macCatalyst 56.0, *)
52+
func introducedLaterOnIOS() {
53+
}
54+
5155
// expected-note@+1 *{{add @available attribute to enclosing global function}}
5256
func testPoundAvailable() {
5357

5458
if #available(macCatalyst 55.0, *) {
5559
introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}}
5660
// expected-note@-1 {{add 'if #available' version check}}
61+
introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}}
62+
// expected-note@-1 {{add 'if #available' version check}}
5763
}
5864

5965
// macCatalyst should win over iOS when present
6066

6167
if #available(iOS 56.0, macCatalyst 55.0, *) {
6268
introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}}
6369
// expected-note@-1 {{add 'if #available' version check}}
70+
introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}}
71+
// expected-note@-1 {{add 'if #available' version check}}
6472
}
6573

6674
if #available(iOS 55.0, macCatalyst 56.0, *) {
6775
introducedLaterOnMacCatalyst() // no-warning
76+
introducedLaterOnIOS() // no-error
77+
}
78+
79+
if #available(iOS 57.0, macCatalyst 56.0, *) {
80+
introducedLaterOnMacCatalyst() // no-warning
81+
introducedLaterOnIOS() // no-error
6882
}
6983

7084
// iOS availability should be inherited when macCatalyst is not present
7185

7286
if #available(iOS 55.0, *) {
7387
introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}}
7488
// expected-note@-1 {{add 'if #available' version check}}
89+
introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}}
90+
// expected-note@-1 {{add 'if #available' version check}}
7591
}
7692

7793
if #available(iOS 56.0, *) {
7894
introducedLaterOnMacCatalyst() // no-warning
95+
introducedLaterOnIOS() // no-error
7996
}
8097

8198
// macOS availability doesn't count on macCatalyst for Swift.

0 commit comments

Comments
 (0)