diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 4512b15047735..7eacce69db72e 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -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 AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { - Optional AnnotatedRange; + const AvailableAttr *bestAvailAttr = nullptr; for (auto Attr : D->getAttrs()) { auto *AvailAttr = dyn_cast(Attr); @@ -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, diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift index 3c3bf676f1c8b..07fd5229471d1 100644 --- a/test/attr/attr_availability_maccatalyst.swift +++ b/test/attr/attr_availability_maccatalyst.swift @@ -48,12 +48,18 @@ 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 @@ -61,10 +67,18 @@ func testPoundAvailable() { 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 @@ -72,10 +86,13 @@ func testPoundAvailable() { 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.