Skip to content

Sema: Allow AnyColorBox in SwiftUI to derive from a less available base class #62327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
4 changes: 4 additions & 0 deletions include/swift/AST/Availability.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ class UnavailabilityReason {
const VersionRange &getRequiredOSVersionRange() const {
return RequiredDeploymentRange;
}

/// Returns true if the required OS version range's lower endpoint is at or
/// below the deployment target of the given ASTContext.
bool requiresDeploymentTargetOrEarlier(ASTContext &Ctx) const;
};

/// Represents everything that a particular chunk of code may assume about its
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ bool Decl::isSemanticallyUnavailable() const {
return evaluateOrDefault(eval, IsSemanticallyUnavailableRequest{this}, false);
}

bool UnavailabilityReason::requiresDeploymentTargetOrEarlier(
ASTContext &Ctx) const {
return RequiredDeploymentRange.getLowerEndpoint() <=
AvailabilityContext::forDeploymentTarget(Ctx)
.getOSVersion()
.getLowerEndpoint();
}

AvailabilityContext
AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr,
ASTContext &ctx) {
Expand Down
18 changes: 10 additions & 8 deletions lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1887,15 +1887,17 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
DeclAvailabilityFlags flags =
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;

// As a concession to source compatibility for API libraries, downgrade
// diagnostics about inheritance from a less available type when the
// following conditions are met:
// 1. The inherited type is only potentially unavailable before the
// deployment target.
// 2. The inheriting type is `@usableFromInline`.
if (nominal->getAttrs().hasAttribute<UsableFromInlineAttr>())
// Allow the `AnyColorBox` class in SwiftUI to inherit from a less available
// super class as a one-off source compatibility exception. Availability
// checking generally doesn't support a more available class deriving from
// a less available base class in a library evolution enabled module, even
// when the base class is available at the deployment target, but this
// declaration slipped in when the compiler wasn't able to diagnose it and
// can't be changed.
if (nominal->getName().is("AnyColorBox") &&
nominal->getModuleContext()->getName().is("SwiftUI"))
flags |= DeclAvailabilityFlag::
WarnForPotentialUnavailabilityBeforeDeploymentTarget;
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget;

llvm::for_each(nominal->getInherited(), [&](TypeLoc inherited) {
checkType(inherited.getType(), inherited.getTypeRepr(), nominal,
Expand Down
20 changes: 10 additions & 10 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2067,9 +2067,7 @@ static Diagnostic getPotentialUnavailabilityDiagnostic(
auto Platform = prettyPlatformString(targetPlatform(Context.LangOpts));
auto Version = Reason.getRequiredOSVersionRange().getLowerEndpoint();

if (Version <= AvailabilityContext::forDeploymentTarget(Context)
.getOSVersion()
.getLowerEndpoint()) {
if (Reason.requiresDeploymentTargetOrEarlier(Context)) {
// The required OS version is at or before the deployment target so this
// diagnostic should indicate that the decl could be unavailable to clients
// of the module containing the reference.
Expand All @@ -2090,7 +2088,7 @@ bool TypeChecker::diagnosePotentialUnavailability(
const ValueDecl *D, SourceRange ReferenceRange,
const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason,
bool WarnBeforeDeploymentTarget) {
bool WarnBeforeDeploymentTarget = false) {
ASTContext &Context = ReferenceDC->getASTContext();

auto RequiredRange = Reason.getRequiredOSVersionRange();
Expand Down Expand Up @@ -3724,18 +3722,20 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
if (!maybeUnavail.has_value())
return false;

auto unavailReason = maybeUnavail.value();
auto *DC = Where.getDeclContext();
if (Flags.contains(
DeclAvailabilityFlag::
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget) &&
unavailReason.requiresDeploymentTargetOrEarlier(DC->getASTContext()))
return false;

if (accessor) {
bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout);
TypeChecker::diagnosePotentialAccessorUnavailability(
accessor, R, DC, maybeUnavail.value(), forInout);
accessor, R, DC, unavailReason, forInout);
} else {
bool downgradeBeforeDeploymentTarget = Flags.contains(
DeclAvailabilityFlag::
WarnForPotentialUnavailabilityBeforeDeploymentTarget);
if (!TypeChecker::diagnosePotentialUnavailability(
D, R, DC, maybeUnavail.value(), downgradeBeforeDeploymentTarget))
if (!TypeChecker::diagnosePotentialUnavailability(D, R, DC, unavailReason))
return false;
}

Expand Down
8 changes: 3 additions & 5 deletions lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,9 @@ enum class DeclAvailabilityFlag : uint8_t {
/// warning. Used for ObjC key path components.
ForObjCKeyPath = 1 << 3,

/// Downgrade errors about decl availability to warnings when the fix would be
/// to constrain availability to a version that is more available than the
/// current deployment target. This is needed for source compatibility in when
/// checking public extensions in library modules.
WarnForPotentialUnavailabilityBeforeDeploymentTarget = 1 << 4,
/// Do not diagnose potential decl unavailability if that unavailability
/// would only occur at or below the deployment target.
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget = 1 << 4,
};
using DeclAvailabilityFlags = OptionSet<DeclAvailabilityFlag>;

Expand Down
15 changes: 15 additions & 0 deletions test/Sema/availability_swiftui.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %target-typecheck-verify-swift -verify -target %target-cpu-apple-macosx12.0 -module-name SwiftUI -library-level api
// RUN: not %target-typecheck-verify-swift -verify -target %target-cpu-apple-macosx12.0 -module-name Other -library-level api

// REQUIRES: OS=macosx

@available(macOS 11, *)
public class LessAvailable {}

@available(macOS 10.15, *)
@usableFromInline
class AnyColorBox: LessAvailable {} // Ok, exception specifically for AnyColorBox

@available(macOS 10.15, *)
@usableFromInline
class OtherClass: LessAvailable {} // expected-error {{'LessAvailable' is only available in macOS 11 or newer; clients of 'SwiftUI' may have a lower deployment target}}
4 changes: 1 addition & 3 deletions test/attr/attr_inlinable_available.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1369,10 +1369,8 @@ public enum NoAvailableEnumWithClasses {
public class InheritsAtDeploymentTarget: AtDeploymentTargetClass {} // expected-error {{'AtDeploymentTargetClass' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}} expected-note 2 {{add @available attribute to enclosing class}}
public class InheritsAfterDeploymentTarget: AfterDeploymentTargetClass {} // expected-error {{'AfterDeploymentTargetClass' is only available in macOS 11 or newer}} expected-note 2 {{add @available attribute to enclosing class}}

// As a special case, downgrade the less available superclasses diagnostic for
// `@usableFromInline` classes.
@usableFromInline
class UFIInheritsBetweenTargets: BetweenTargetsClass {} // expected-warning {{'BetweenTargetsClass' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}} expected-note 2 {{add @available attribute to enclosing class}}
class UFIInheritsBetweenTargets: BetweenTargetsClass {} // expected-error {{'BetweenTargetsClass' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}} expected-note 2 {{add @available attribute to enclosing class}}
}

@_spi(Private)
Expand Down