Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,29 @@ void AvailabilityInference::applyInferredAvailableAttrs(
}
}

/// Returns true if the introduced version in \p newAttr should be used instead
/// of the introduced version in \p prevAttr when both are attached to the same
/// declaration and refer to the active platform.
static bool isBetterThan(const AvailableAttr *newAttr,
const AvailableAttr *prevAttr) {
assert(newAttr);

// If there is no prevAttr, newAttr of course wins.
if (!prevAttr)
return true;

// If they belong to the same platform, the one that introduces later wins.
if (prevAttr->Platform == newAttr->Platform)
return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue();

// If the new attribute's platform inherits from the old one, it wins.
return inheritsAvailabilityFromPlatform(newAttr->Platform,
prevAttr->Platform);
}

Optional<AvailabilityContext>
AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
Optional<AvailabilityContext> AnnotatedRange;
const AvailableAttr *bestAvailAttr = nullptr;

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

AvailabilityContext AttrRange{
VersionRange::allGTE(AvailAttr->Introduced.getValue())};

// If we have multiple introduction versions, we will conservatively
// assume the worst case scenario. We may want to be more precise here
// in the future or emit a diagnostic.

if (AnnotatedRange.hasValue()) {
AnnotatedRange.getValue().intersectWith(AttrRange);
} else {
AnnotatedRange = AttrRange;
}
if (isBetterThan(AvailAttr, bestAvailAttr))
bestAvailAttr = AvailAttr;
}

return AnnotatedRange;
if (!bestAvailAttr)
return None;

return AvailabilityContext{
VersionRange::allGTE(bestAvailAttr->Introduced.getValue())};
}

AvailabilityContext AvailabilityInference::availableRange(const Decl *D,
Expand Down
17 changes: 17 additions & 0 deletions test/attr/attr_availability_maccatalyst.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,34 +48,51 @@ deprecatedOnIOSButNotMacCatalyst() // no-warning
func introducedLaterOnMacCatalyst() {
}

@available(iOS 57.0, macCatalyst 56.0, *)
func introducedLaterOnIOS() {
}

// expected-note@+1 *{{add @available attribute to enclosing global function}}
func testPoundAvailable() {

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

// macCatalyst should win over iOS when present

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

if #available(iOS 55.0, macCatalyst 56.0, *) {
introducedLaterOnMacCatalyst() // no-warning
introducedLaterOnIOS() // no-error
}

if #available(iOS 57.0, macCatalyst 56.0, *) {
introducedLaterOnMacCatalyst() // no-warning
introducedLaterOnIOS() // no-error
}

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

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

if #available(iOS 56.0, *) {
introducedLaterOnMacCatalyst() // no-warning
introducedLaterOnIOS() // no-error
}

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