diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3b222cc32cf25..056d923a88d44 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2455,10 +2455,16 @@ NOTE(suggest_removing_override, none, ERROR(override_less_available,none, "overriding %0 must be as available as declaration it overrides", (DeclBaseName)) +WARNING(override_less_available_warn,none, + "overriding %0 must be as available as declaration it overrides", + (DeclBaseName)) ERROR(override_accessor_less_available,none, "overriding %0 for %1 must be as available as declaration it overrides", (DescriptiveDeclKind, DeclBaseName)) +WARNING(override_accessor_less_available_warn,none, + "overriding %0 for %1 must be as available as declaration it overrides", + (DescriptiveDeclKind, DeclBaseName)) ERROR(override_let_property,none, "cannot override immutable 'let' property %0 with the getter of a 'var'", diff --git a/include/swift/AST/TypeRefinementContext.h b/include/swift/AST/TypeRefinementContext.h index 2dca10739444e..c0851969fd712 100644 --- a/include/swift/AST/TypeRefinementContext.h +++ b/include/swift/AST/TypeRefinementContext.h @@ -154,16 +154,23 @@ class TypeRefinementContext { SourceRange SrcRange; + /// Runtime availability information for the code in this context. AvailabilityContext AvailabilityInfo; + /// Runtime availability information as explicitly declared by attributes + /// for the inlinable code in this context. Compared to AvailabilityInfo, + /// this is not bounded to the minimum deployment OS version. + AvailabilityContext AvailabilityInfoExplicit; + std::vector Children; TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info); + const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit); public: - + /// Create the root refinement context for the given SourceFile. static TypeRefinementContext *createRoot(SourceFile *SF, const AvailabilityContext &Info); @@ -172,8 +179,9 @@ class TypeRefinementContext { static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit, SourceRange SrcRange); - + /// Create a refinement context for the Then branch of the given IfStmt. static TypeRefinementContext * createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent, @@ -240,11 +248,19 @@ class TypeRefinementContext { SourceRange getSourceRange() const { return SrcRange; } /// Returns the information on what can be assumed present at run time when - /// running code contained in this context. + /// running code contained in this context, taking into account the minimum + /// deployment target. const AvailabilityContext &getAvailabilityInfo() const { return AvailabilityInfo; } + /// Returns the information on what can be assumed present at run time when + /// running code contained in this context if it were to be inlined, + /// without considering the minimum deployment target. + const AvailabilityContext &getAvailabilityInfoExplicit() const { + return AvailabilityInfoExplicit; + } + /// Adds a child refinement context. void addChild(TypeRefinementContext *Child) { assert(Child->getSourceRange().isValid()); diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index e042a3dda668f..20e9fb6132ec1 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -144,8 +144,13 @@ static bool isBetterThan(const AvailableAttr *newAttr, return true; // If they belong to the same platform, the one that introduces later wins. - if (prevAttr->Platform == newAttr->Platform) + if (prevAttr->Platform == newAttr->Platform) { + if (newAttr->isUnconditionallyUnavailable()) + return true; + if (prevAttr->isUnconditionallyUnavailable()) + return false; return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue(); + } // If the new attribute's platform inherits from the old one, it wins. return inheritsAvailabilityFromPlatform(newAttr->Platform, @@ -158,10 +163,12 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { for (auto Attr : D->getAttrs()) { auto *AvailAttr = dyn_cast(Attr); - if (AvailAttr == nullptr || !AvailAttr->Introduced.hasValue() || + if (AvailAttr == nullptr || !AvailAttr->isActivePlatform(Ctx) || AvailAttr->isLanguageVersionSpecific() || - AvailAttr->isPackageDescriptionVersionSpecific()) { + AvailAttr->isPackageDescriptionVersionSpecific() || + (!AvailAttr->Introduced.hasValue() && + !AvailAttr->isUnconditionallyUnavailable())) { continue; } @@ -172,6 +179,9 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { if (!bestAvailAttr) return None; + if (bestAvailAttr->isUnconditionallyUnavailable()) + return AvailabilityContext(VersionRange::empty()); + return AvailabilityContext{ VersionRange::allGTE(bestAvailAttr->Introduced.getValue())}; } diff --git a/lib/AST/TypeRefinementContext.cpp b/lib/AST/TypeRefinementContext.cpp index c5bcaeb4e0d54..3fe283bebd896 100644 --- a/lib/AST/TypeRefinementContext.cpp +++ b/lib/AST/TypeRefinementContext.cpp @@ -28,12 +28,14 @@ using namespace swift; TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info) - : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) { + const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit) + : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info), + AvailabilityInfoExplicit(InfoExplicit) { if (Parent) { assert(SrcRange.isValid()); Parent->addChild(this); - assert(Info.isContainedIn(Parent->getAvailabilityInfo())); + assert(InfoExplicit.isContainedIn(Parent->getAvailabilityInfoExplicit())); } Ctx.addDestructorCleanup(Children); } @@ -46,18 +48,20 @@ TypeRefinementContext::createRoot(SourceFile *SF, ASTContext &Ctx = SF->getASTContext(); return new (Ctx) TypeRefinementContext(Ctx, SF, - /*Parent=*/nullptr, SourceRange(), Info); + /*Parent=*/nullptr, SourceRange(), Info, + AvailabilityContext::alwaysAvailable()); } TypeRefinementContext * TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit, SourceRange SrcRange) { assert(D); assert(Parent); return new (Ctx) - TypeRefinementContext(Ctx, D, Parent, SrcRange, Info); + TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, InfoExplicit); } TypeRefinementContext * @@ -68,7 +72,7 @@ TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent, - S->getThenStmt()->getSourceRange(), Info); + S->getThenStmt()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -79,7 +83,7 @@ TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent, - S->getElseStmt()->getSourceRange(), Info); + S->getElseStmt()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -92,7 +96,7 @@ TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx, assert(Parent); SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range, - Info); + Info, Info); } TypeRefinementContext * @@ -107,7 +111,7 @@ TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx, SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/true), - Parent, Range, Info); + Parent, Range, Info, Info); } TypeRefinementContext * @@ -118,7 +122,7 @@ TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent, - RS->getBody()->getSourceRange(), Info); + RS->getBody()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -128,7 +132,7 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S, assert(S); assert(Parent); return new (Ctx) TypeRefinementContext( - Ctx, S, Parent, S->getBody()->getSourceRange(), Info); + Ctx, S, Parent, S->getBody()->getSourceRange(), Info, Info); } // Only allow allocation of TypeRefinementContext using the allocator in @@ -296,6 +300,7 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr, OS << "(" << getReasonName(getReason()); OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString(); + OS << " explicit=" << AvailabilityInfoExplicit.getOSVersion().getAsString(); if (getReason() == Reason::Decl) { Decl *D = Node.getAsDecl(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 079ded2e02094..81cb3ee453050 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1817,18 +1817,27 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info, if (info.isAlwaysAvailable()) return; + PlatformAgnosticAvailabilityKind platformAgnosticAvailability; + llvm::VersionTuple introducedVersion; + if (info.isKnownUnreachable()) { + platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::Unavailable; + } else { + platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::None; + introducedVersion = info.getOSVersion().getLowerEndpoint(); + } + llvm::VersionTuple noVersion; auto AvAttr = new (C) AvailableAttr(SourceLoc(), SourceRange(), targetPlatform(C.LangOpts), /*Message=*/StringRef(), /*Rename=*/StringRef(), - info.getOSVersion().getLowerEndpoint(), + introducedVersion, /*IntroducedRange*/SourceRange(), /*Deprecated=*/noVersion, /*DeprecatedRange*/SourceRange(), /*Obsoleted=*/noVersion, /*ObsoletedRange*/SourceRange(), - PlatformAgnosticAvailabilityKind::None, + platformAgnosticAvailability, /*Implicit=*/false); decl->getAttrs().add(AvAttr); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f08d0cfc025f3..2ba2f076e9a1b 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2638,7 +2638,8 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (isAlwaysWeakImported()) OS << "[weak_imported] "; auto availability = getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable()) { + if (!availability.isAlwaysAvailable() && + !availability.isKnownUnreachable()) { auto version = availability.getOSVersion().getLowerEndpoint(); OS << "[available " << version.getAsString() << "] "; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index ff1db56c28832..4342772a50201 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1530,9 +1530,16 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { VersionRange::allGTE(attr->Introduced.getValue())}; if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) { - diagnose(attr->getLocation(), diag::availability_decl_more_than_enclosing); - diagnose(EnclosingDecl->getLoc(), - diag::availability_decl_more_than_enclosing_enclosing_here); + // Don't show this error in swiftinterfaces if it is about a context that + // is unavailable, this was in the stdlib at Swift 5.3. + SourceFile *Parent = D->getDeclContext()->getParentSourceFile(); + if (!Parent || Parent->Kind != SourceFileKind::Interface || + !EnclosingAnnotatedRange.getValue().isKnownUnreachable()) { + diagnose(attr->getLocation(), + diag::availability_decl_more_than_enclosing); + diagnose(EnclosingDecl->getLoc(), + diag::availability_decl_more_than_enclosing_enclosing_here); + } } } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index f99a9415f551c..e072bf594ba84 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -170,13 +170,30 @@ class TypeRefinementContextBuilder : private ASTWalker { // The potential versions in the declaration are constrained by both // the declared availability of the declaration and the potential versions // of its lexical context. - AvailabilityContext DeclInfo = + AvailabilityContext ExplicitDeclInfo = swift::AvailabilityInference::availableRange(D, Context); - DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo()); + ExplicitDeclInfo.intersectWith( + getCurrentTRC()->getAvailabilityInfoExplicit()); + AvailabilityContext DeclInfo = ExplicitDeclInfo; + + // When the body is inlinable consider only the explicitly declared range + // for checking availability. Otherwise, use the parent range which may + // begin at the minimum deployment target. + // + // Also use the parent range when reading swiftinterfaces for + // retrocompatibility. + bool isInlinable = D->getAttrs().hasAttribute() || + D->getAttrs().hasAttribute(); + SourceFile *SF = D->getDeclContext()->getParentSourceFile(); + if (!isInlinable || (SF && SF->Kind == SourceFileKind::Interface)) { + DeclInfo.intersectWith( + getCurrentTRC()->getAvailabilityInfo()); + } TypeRefinementContext *NewTRC = TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(), DeclInfo, + ExplicitDeclInfo, refinementSourceRangeForDecl(D)); // Record the TRC for this storage declaration so that @@ -190,19 +207,27 @@ class TypeRefinementContextBuilder : private ASTWalker { return NewTRC; } - + /// Returns true if the declaration should introduce a new refinement context. bool declarationIntroducesNewContext(Decl *D) { if (!isa(D) && !isa(D)) { return false; } - + + // Explicit inlinability may to the decl being used on an earlier OS + // version when inlined on the client side. This check assumes that + // implicit decls are handled elsewhere. + bool isExplicitlyInlinable = !D->isImplicit() && + (D->getAttrs().hasAttribute() || + D->getAttrs().hasAttribute()); + // No need to introduce a context if the declaration does not have an - // availability attribute. - if (!hasActiveAvailableAttribute(D, Context)) { + // availability or non-implicit inlinable attribute. + if (!hasActiveAvailableAttribute(D, Context) && + !isExplicitlyInlinable) { return false; } - + // Only introduce for an AbstractStorageDecl if it is not local. // We introduce for the non-local case because these may // have getters and setters (and these may be synthesized, so they might @@ -213,7 +238,7 @@ class TypeRefinementContextBuilder : private ASTWalker { return false; } } - + return true; } @@ -674,9 +699,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, // refined. For now, this is fine -- but if we ever synthesize #available(), // this will be a real problem. - // We can assume we are running on at least the minimum deployment target. - auto OverApproximateContext = - AvailabilityContext::forDeploymentTarget(Context); + auto OverApproximateContext = AvailabilityContext::alwaysAvailable(); auto isInvalidLoc = [SF](SourceLoc loc) { return SF ? loc.isInvalid() : true; }; @@ -707,6 +730,14 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, } } + // If we still don't have an introduction version, use the current deployment + // target. This covers cases where an inlinable function and its parent + // contexts don't have explicit availability attributes. + if (!OverApproximateContext.getOSVersion().hasLowerEndpoint()) { + auto currentOS = AvailabilityContext::forDeploymentTarget(Context); + OverApproximateContext.constrainWith(currentOS); + } + return OverApproximateContext; } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index ca957db149544..33085e514644b 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1687,16 +1687,37 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override, if (isRedundantAccessorOverrideAvailabilityDiagnostic(override, base)) return false; - auto &diags = override->getASTContext().Diags; + auto &ctx = override->getASTContext(); + auto &diags = ctx.Diags; if (auto *accessor = dyn_cast(override)) { - diags.diagnose(override, diag::override_accessor_less_available, + bool downgradeToWarning = override->getAttrs().isUnavailable(ctx); + diags.diagnose(override, + downgradeToWarning? + diag::override_accessor_less_available_warn : + diag::override_accessor_less_available, accessor->getDescriptiveKind(), accessor->getStorage()->getBaseName()); diags.diagnose(base, diag::overridden_here); return true; } - diags.diagnose(override, diag::override_less_available, + bool downgradeToWarning = false; + if (override->getAttrs().isUnavailable(ctx)) { + // Don't report constructors that are marked unavailable as being less + // available than their introduction. This was previously allowed and + // can be used to forbid the direct use of a constructor in a subclass. + // Note that even when marked unavailable the constructor could be called + // by other inherited constructors. + if (isa(override)) + return false; + + // Report as a warning other unavailable overrides. + downgradeToWarning = true; + } + + diags.diagnose(override, + downgradeToWarning? diag::override_less_available_warn : + diag::override_less_available, override->getBaseName()); diags.diagnose(base, diag::overridden_here); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index dc0c1c182f14a..72d0faaf2227c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1469,6 +1469,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, return CheckKind::UsableFromInline; } + if (match.Witness->getAttrs().isUnavailable(getASTContext()) && + !requirement->getAttrs().isUnavailable(getASTContext())) { + return CheckKind::WitnessUnavailable; + } + auto requiredAvailability = AvailabilityContext::alwaysAvailable(); if (checkWitnessAvailability(requirement, match.Witness, &requiredAvailability)) { @@ -1498,11 +1503,6 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, } } - if (match.Witness->getAttrs().isUnavailable(getASTContext()) && - !requirement->getAttrs().isUnavailable(getASTContext())) { - return CheckKind::WitnessUnavailable; - } - return CheckKind::Success; } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index b651fcae6698d..eb7e086679a00 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -430,7 +430,8 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { Optional available; auto availability = F.getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable()) { + if (!availability.isAlwaysAvailable() && + !availability.isKnownUnreachable()) { available = availability.getOSVersion().getLowerEndpoint(); } ENCODE_VER_TUPLE(available, available) diff --git a/test/SILGen/vtables.swift b/test/SILGen/vtables.swift index 387fd0df1e69d..9c0f2fafc7369 100644 --- a/test/SILGen/vtables.swift +++ b/test/SILGen/vtables.swift @@ -21,7 +21,6 @@ class C : B { // CHECK: #A.bar: {{.*}} : @$s7vtables1CC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1CC3qux{{[_0-9a-zA-Z]*}}F -// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1CC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1CC4zang{{[_0-9a-zA-Z]*}}F @@ -35,7 +34,7 @@ class A { func bar() {} func bas() {} func qux() {} - func flux() {} + @available(*, unavailable) func flux() {} } // CHECK: sil_vtable A { @@ -54,7 +53,6 @@ class B : A { // bar inherited from A // bas inherited from A override func qux() {} - @available(*, unavailable) override func flux() {} func zim() {} func zang() {} @@ -65,7 +63,6 @@ class B : A { // CHECK: #A.bar: {{.*}} : @$s7vtables1AC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1BC3qux{{[_0-9a-zA-Z]*}}F -// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1BC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1BC4zang{{[_0-9a-zA-Z]*}}F diff --git a/test/Sema/availability_inlinable.swift b/test/Sema/availability_inlinable.swift new file mode 100644 index 0000000000000..34750bce1134e --- /dev/null +++ b/test/Sema/availability_inlinable.swift @@ -0,0 +1,48 @@ +/// Inlinable functions should check availability by ignoring the current +/// deployment target as clients could inline the function in a lower target. + +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx11.0 +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.10 + +// REQUIRES: OS=macosx + +@available(macOS 10.10, *) +@inlinable +public func availMacOS10() { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + + if #available(macOS 11.0, *) { + availMacOS11() + } else { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } + + if #available(macOS 10.15, *) { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } + + func nestedFunc() { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } +} + +@available(macOS 11.0, *) +public func availMacOS11() { } + +@available(macOS 10.10, *) +public struct StructAvailMacOS10 { + @inlinable + public func availabilityFromTheContextInlinable() { + // expected-note @-1 {{add @available attribute to enclosing instance method}} + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + + availabilityFromContext() + } + + public func availabilityFromContext() {} +} diff --git a/test/Sema/availability_refinement_contexts.swift b/test/Sema/availability_refinement_contexts.swift index 088a644384ff6..44f6b8fd05f6e 100644 --- a/test/Sema/availability_refinement_contexts.swift +++ b/test/Sema/availability_refinement_contexts.swift @@ -1,18 +1,18 @@ -// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s > %t.dump 2>&1 +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts -target %target-cpu-apple-macosx10.50 %s > %t.dump 2>&1 // RUN: %FileCheck --strict-whitespace %s < %t.dump // REQUIRES: OS=macosx -// CHECK: {{^}}(root versions=[10.{{[0-9]+}}.0,+Inf) +// CHECK: {{^}}(root versions=[10.50.0,+Inf) explicit=all -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=someInnerFunc() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=InnerClass -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=innerClassMethod -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someStaticProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someComputedProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someOtherMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=someInnerFunc() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=InnerClass +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=innerClassMethod +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someStaticProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someComputedProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someOtherMethod() @available(OSX 10.51, *) class SomeClass { @available(OSX 10.52, *) @@ -43,13 +43,13 @@ class SomeClass { func someOtherMethod() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=someFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=someFunction() @available(OSX 10.51, *) func someFunction() { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeProtocol -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoProperty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeProtocol +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoProperty @available(OSX 10.51, *) protocol SomeProtocol { @available(OSX 10.52, *) @@ -59,26 +59,26 @@ protocol SomeProtocol { var protoProperty: Int { get } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someExtensionFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someExtensionFunction() @available(OSX 10.51, *) extension SomeClass { @available(OSX 10.52, *) func someExtensionFunction() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) decl=funcInGuardElse() -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) explicit=[10.55,+Inf) decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) explicit=[10.56,+Inf) +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) explicit=[10.56,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) explicit=[10.57,+Inf) decl=funcInInnerIfElse() @available(OSX 10.51, *) func functionWithStmtCondition() { if #available(OSX 10.52, *), @@ -97,11 +97,11 @@ func functionWithStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) @available(OSX 10.51, *) func functionWithUnnecessaryStmtCondition() { @@ -127,13 +127,13 @@ func functionWithUnnecessaryStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch -// CHECK-NEXT: {{^}} (if_else versions=empty -// CHECK-NEXT: {{^}} (decl versions=empty decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (if_else versions=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty -// CHECK-NEXT: {{^}} (if_else versions=empty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty @available(OSX 10.51, *) func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { @@ -180,10 +180,10 @@ func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithWhile() -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=funcInWhileBody() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithWhile() +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=funcInWhileBody() @available(OSX 10.51, *) func functionWithWhile() { while #available(OSX 10.52, *), @@ -192,3 +192,49 @@ func functionWithWhile() { func funcInWhileBody() { } } } + +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=olderFunction() +@available(macOS 10.10, *) +public func olderFunction() { +} + +// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=unavailableFunction() +@available(macOS, unavailable) +public func unavailableFunction() { +} + +// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=inlinableFunction() +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.15,+Inf) explicit=[10.15,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) explicit=[10.15,+Inf) +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.15,+Inf) explicit=[10.15,+Inf) +@available(macOS 10.10, *) +@inlinable +public func inlinableFunction() { + if #available(macOS 10.55, *) { } else { } + + if #available(macOS 10.15, *) { } + + guard #available(macOS 10.15, *) else { } + + func nestedFunc() { } +} + +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=SomeOlderClass +// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=someInlinableMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.15,+Inf) explicit=[10.15,+Inf) decl=someOtherInlinableMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.15,+Inf) decl=someMethod() +@available(OSX 10.10, *) +class SomeOlderClass { + @inlinable + func someInlinableMethod() { } + + @available(OSX 10.15, *) + @inlinable + func someOtherInlinableMethod() { } + + @available(OSX 10.15, *) + func someMethod() { } +} diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index ad4159215becf..97ec079f50d06 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -788,26 +788,33 @@ class UnavailableClassExtendingUnavailableClass : ClassAvailableOn10_51 { // Method availability is contravariant class SuperWithAlwaysAvailableMembers { - func shouldAlwaysBeAvailableMethod() { // expected-note {{overridden declaration is here}} + + required init() {} // expected-note {{overridden declaration is here}} + + func shouldAlwaysBeAvailableMethod() { // expected-note 2 {{overridden declaration is here}} } - var shouldAlwaysBeAvailableProperty: Int { // expected-note {{overridden declaration is here}} + var shouldAlwaysBeAvailableProperty: Int { // expected-note 2 {{overridden declaration is here}} get { return 9 } set(newVal) {} } var setterShouldAlwaysBeAvailableProperty: Int { get { return 9 } - set(newVal) {} // expected-note {{overridden declaration is here}} + set(newVal) {} // expected-note 2 {{overridden declaration is here}} } var getterShouldAlwaysBeAvailableProperty: Int { - get { return 9 } // expected-note {{overridden declaration is here}} + get { return 9 } // expected-note 2 {{overridden declaration is here}} set(newVal) {} } } class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { + + @available(OSX, introduced: 10.51) + required init() {} // expected-error {{overriding 'init' must be as available as declaration it overrides}} + @available(OSX, introduced: 10.51) override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} } @@ -832,6 +839,35 @@ class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { } } +class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { + + @available(OSX, unavailable) + required init() {} + + @available(OSX, unavailable) + override func shouldAlwaysBeAvailableMethod() { // expected-warning {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} + } + + @available(OSX, unavailable) + override var shouldAlwaysBeAvailableProperty: Int { // expected-warning {{overriding 'shouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + get { return 10 } + set(newVal) {} + } + + override var setterShouldAlwaysBeAvailableProperty: Int { + get { return 9 } + @available(OSX, unavailable) + set(newVal) {} // expected-warning {{overriding setter for 'setterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + // This is a terrible diagnostic. rdar://problem/20427938 + } + + override var getterShouldAlwaysBeAvailableProperty: Int { + @available(OSX, unavailable) + get { return 9 } // expected-warning {{overriding getter for 'getterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + set(newVal) {} + } +} + class SuperWithLimitedMemberAvailability { @available(OSX, introduced: 10.51) func someMethod() { diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift index f69cc8aea1b3d..2346cb9ec0dfc 100644 --- a/test/attr/attr_availability_maccatalyst.swift +++ b/test/attr/attr_availability_maccatalyst.swift @@ -1,6 +1,6 @@ // RUN: %swift -typecheck -verify -parse-stdlib -target x86_64-apple-ios51.0-macabi %s -// REQUIRES: OS=maccatalyst +// REQUIRES: VENDOR=apple @available(macCatalyst, introduced: 1.0, deprecated: 2.0, obsoleted: 9.0, message: "you don't want to do that anyway") @@ -52,6 +52,16 @@ func introducedLaterOnMacCatalyst() { func introducedLaterOnIOS() { } +@available(iOS, introduced: 1.0) +@available(macCatalyst, unavailable) +func unavailableOnMacCatalyst() { // expected-note 3 {{'unavailableOnMacCatalyst()' has been explicitly marked unavailable here}} +} + +@available(iOS, unavailable) +@available(macCatalyst, introduced: 1.0) +func unavailableOnIOS() { +} + // expected-note@+1 *{{add @available attribute to enclosing global function}} func testPoundAvailable() { @@ -60,6 +70,9 @@ func testPoundAvailable() { // 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}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } // macCatalyst should win over iOS when present @@ -69,6 +82,9 @@ func testPoundAvailable() { // 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}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } if #available(iOS 55.0, macCatalyst 56.0, *) { @@ -88,6 +104,9 @@ func testPoundAvailable() { // 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}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } if #available(iOS 56.0, *) {