Skip to content

AST: Remove DeclAttributes::getPotentiallyUnavailable() #77720

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
merged 7 commits into from
Nov 20, 2024
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
5 changes: 0 additions & 5 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2932,11 +2932,6 @@ class DeclAttributes {
findMostSpecificActivePlatform(const ASTContext &ctx,
bool ignoreAppExtensions = false) const;

/// Returns the first @available attribute that indicates
/// a declaration is unavailable, or the first one that indicates it's
/// potentially unavailable, or null otherwise.
const AvailableAttr *getPotentiallyUnavailable(const ASTContext &ctx) const;

/// Returns the first @available attribute that indicates
/// a declaration is unavailable, or null otherwise.
const AvailableAttr *getUnavailable(const ASTContext &ctx,
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/AvailabilityConstraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class AvailabilityConstraint {
Kind getKind() const { return kind; }
const AvailableAttr *getAttr() const { return attr; }

/// Returns the platform that this constraint applies to, or
/// `PlatformKind::none` if it is not platform specific.
PlatformKind getPlatform() const;

/// Returns the required range for `IntroducedInNewerVersion` requirements, or
/// `std::nullopt` otherwise.
std::optional<AvailabilityRange>
Expand All @@ -95,6 +99,10 @@ class AvailabilityConstraint {
/// Returns true if this unmet requirement can be satisfied by introducing an
/// `if #available(...)` condition in source.
bool isConditionallySatisfiable() const;

/// Some availability constraints are active for type-checking but cannot
/// be translated directly into an `if #available(...)` runtime query.
bool isActiveForRuntimeQueries(ASTContext &ctx) const;
};

} // end namespace swift
Expand Down
21 changes: 15 additions & 6 deletions include/swift/AST/AvailabilityContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,27 @@ class AvailabilityContext {

AvailabilityContext(const Storage *info) : Info(info) { assert(info); };

/// Retrieves a uniqued `AvailabilityContext` with the given platform
/// availability parameters.
/// Retrieves an `AvailabilityContext` with the given platform availability
/// parameters.
static AvailabilityContext
get(const AvailabilityRange &platformAvailability,
std::optional<PlatformKind> unavailablePlatform, bool deprecated,
ASTContext &ctx);

public:
/// Retrieves the default `AvailabilityContext`, which is maximally available.
/// The platform availability range will be set to the deployment target (or
/// minimum inlining target when applicable).
static AvailabilityContext getDefault(ASTContext &ctx);
/// Retrieves an `AvailabilityContext` constrained by the given platform
/// availability range.
static AvailabilityContext forPlatformRange(const AvailabilityRange &range,
ASTContext &ctx);

/// Retrieves the maximally available `AvailabilityContext` for the
/// compilation. The platform availability range will be set to the minimum
/// inlining target (which may just be the deployment target).
static AvailabilityContext forInliningTarget(ASTContext &ctx);

/// Retrieves an `AvailabilityContext` with the platform availability range
/// set to the deployment target.
static AvailabilityContext forDeploymentTarget(ASTContext &ctx);

/// Returns the range of platform versions which may execute code in the
/// availability context, starting at its introduction version.
Expand Down
42 changes: 0 additions & 42 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,48 +427,6 @@ DeclAttributes::findMostSpecificActivePlatform(const ASTContext &ctx,
return bestAttr;
}

const AvailableAttr *
DeclAttributes::getPotentiallyUnavailable(const ASTContext &ctx) const {
const AvailableAttr *potential = nullptr;
const AvailableAttr *conditional = nullptr;

for (auto Attr : *this)
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
if (AvAttr->isInvalid())
continue;

if (!AvAttr->isActivePlatform(ctx) &&
!AvAttr->isLanguageVersionSpecific() &&
!AvAttr->isPackageDescriptionVersionSpecific())
continue;

// Definitely not available.
if (AvAttr->isUnconditionallyUnavailable())
return AvAttr;

switch (AvAttr->getVersionAvailability(ctx)) {
case AvailableVersionComparison::Available:
// Doesn't limit the introduced version.
break;

case AvailableVersionComparison::PotentiallyUnavailable:
// We'll return this if we don't see something that proves it's
// not available in this version.
potential = AvAttr;
break;

case AvailableVersionComparison::Unavailable:
case AvailableVersionComparison::Obsoleted:
conditional = AvAttr;
break;
}
}

if (conditional)
return conditional;
return potential;
}

const AvailableAttr *
DeclAttributes::getUnavailable(const ASTContext &ctx,
bool ignoreAppExtensions) const {
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ AvailabilityRange AvailabilityRange::forRuntimeTarget(const ASTContext &Ctx) {
return AvailabilityRange(VersionRange::allGTE(Ctx.LangOpts.RuntimeVersion));
}

PlatformKind AvailabilityConstraint::getPlatform() const {
return attr->Platform;
}

std::optional<AvailabilityRange>
AvailabilityConstraint::getRequiredNewerAvailabilityRange(
ASTContext &ctx) const {
Expand All @@ -89,6 +93,15 @@ bool AvailabilityConstraint::isConditionallySatisfiable() const {
}
}

bool AvailabilityConstraint::isActiveForRuntimeQueries(ASTContext &ctx) const {
if (attr->Platform == PlatformKind::none)
return true;

return swift::isPlatformActive(attr->Platform, ctx.LangOpts,
/*forTargetVariant=*/false,
/*forRuntimeQuery=*/true);
}

namespace {

/// The inferred availability required to access a group of declarations
Expand Down
17 changes: 14 additions & 3 deletions lib/AST/AvailabilityContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,26 @@ void AvailabilityContext::Storage::Profile(llvm::FoldingSetNodeID &id) const {
Platform.Profile(id);
}

AvailabilityContext AvailabilityContext::getDefault(ASTContext &ctx) {
PlatformInfo platformInfo{AvailabilityRange::forInliningTarget(ctx),
PlatformKind::none,
AvailabilityContext
AvailabilityContext::forPlatformRange(const AvailabilityRange &range,
ASTContext &ctx) {
PlatformInfo platformInfo{range, PlatformKind::none,
/*IsUnavailable*/ false,
/*IsUnavailableInEmbedded*/ false,
/*IsDeprecated*/ false};
return AvailabilityContext(Storage::get(platformInfo, ctx));
}

AvailabilityContext AvailabilityContext::forInliningTarget(ASTContext &ctx) {
return AvailabilityContext::forPlatformRange(
AvailabilityRange::forInliningTarget(ctx), ctx);
}

AvailabilityContext AvailabilityContext::forDeploymentTarget(ASTContext &ctx) {
return AvailabilityContext::forPlatformRange(
AvailabilityRange::forDeploymentTarget(ctx), ctx);
}

AvailabilityContext
AvailabilityContext::get(const AvailabilityRange &platformAvailability,
std::optional<PlatformKind> unavailablePlatform,
Expand Down
49 changes: 24 additions & 25 deletions lib/Sema/DerivedConformanceRawRepresentable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
//===----------------------------------------------------------------------===//

#include "CodeSynthesis.h"
#include "DerivedConformances.h"
#include "TypeCheckAvailability.h"
#include "TypeCheckDecl.h"
#include "TypeChecker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "llvm/ADT/APInt.h"
#include "DerivedConformances.h"
#include "TypeCheckDecl.h"

using namespace swift;

Expand Down Expand Up @@ -236,38 +237,35 @@ struct RuntimeVersionCheck {
/// information about the runtime check needed to ensure it is available to
/// \c versionCheck and returns true.
static bool
checkAvailability(const EnumElementDecl *elt, ASTContext &C,
checkAvailability(const EnumElementDecl *elt, DeclContext *dc,
AvailabilityContext availabilityContext,
std::optional<RuntimeVersionCheck> &versionCheck) {
auto *attr = elt->getAttrs().getPotentiallyUnavailable(C);
auto &C = dc->getASTContext();
auto constraint =
getUnsatisfiedAvailabilityConstraint(elt, dc, availabilityContext);

// Is it always available?
if (!attr)
if (!constraint)
return true;

// For type-checking purposes, iOS availability is inherited for visionOS
// targets. However, it is not inherited for the sake of code-generation
// of runtime availability queries, and is assumed to be available.
if ((attr->Platform == PlatformKind::iOS ||
attr->Platform == PlatformKind::iOSApplicationExtension) &&
C.LangOpts.Target.isXROS())
// Some constraints are active for type checking but can't translate to
// runtime restrictions.
if (!constraint->isActiveForRuntimeQueries(C))
return true;

AvailableVersionComparison availability = attr->getVersionAvailability(C);

assert(availability != AvailableVersionComparison::Available &&
"DeclAttributes::getPotentiallyUnavailable() shouldn't "
"return an available attribute");

// Is it never available?
if (availability != AvailableVersionComparison::PotentiallyUnavailable)
if (!constraint->isConditionallySatisfiable())
return false;

// It's conditionally available; create a version constraint and return true.
assert(attr->getPlatformAgnosticAvailability() ==
PlatformAgnosticAvailabilityKind::None &&
"can only express #available(somePlatform version) checks");
versionCheck.emplace(attr->Platform, *attr->Introduced);
auto platform = constraint->getPlatform();
auto range = constraint->getRequiredNewerAvailabilityRange(C);

// Only platform version constraints are supported currently.
ASSERT(platform != PlatformKind::none);
ASSERT(range);

versionCheck.emplace(platform, range->getRawMinimumVersion());
return true;
}

Expand All @@ -294,6 +292,7 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) {

auto parentDC = initDecl->getDeclContext();
ASTContext &C = parentDC->getASTContext();
auto availabilityContext = AvailabilityContext::forDeploymentTarget(C);

auto nominalTypeDecl = parentDC->getSelfNominalTypeDecl();
auto enumDecl = cast<EnumDecl>(nominalTypeDecl);
Expand All @@ -317,7 +316,7 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) {
// information about that check in versionCheck and keep processing this
// element.
std::optional<RuntimeVersionCheck> versionCheck(std::nullopt);
if (!checkAvailability(elt, C, versionCheck))
if (!checkAvailability(elt, parentDC, availabilityContext, versionCheck))
continue;

// litPat = elt.rawValueExpr as a pattern
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1428,7 +1428,7 @@ void TypeChecker::buildAvailabilityScopes(SourceFile &SF) {
// The root availability scope reflects the fact that all parts of
// the source file are guaranteed to be executing on at least the minimum
// platform version for inlining.
auto AvailabilityContext = AvailabilityContext::getDefault(Context);
auto AvailabilityContext = AvailabilityContext::forInliningTarget(Context);
AvailabilityScope *RootScope =
AvailabilityScope::createForSourceFile(&SF, AvailabilityContext);
SF.setAvailabilityScope(RootScope);
Expand Down Expand Up @@ -1493,7 +1493,7 @@ TypeChecker::availabilityAtLocation(SourceLoc loc, const DeclContext *DC,
// this will be a real problem.

// We can assume we are running on at least the minimum inlining target.
auto baseAvailability = AvailabilityContext::getDefault(Context);
auto baseAvailability = AvailabilityContext::forInliningTarget(Context);
auto isInvalidLoc = [SF](SourceLoc loc) {
return SF ? loc.isInvalid() : true;
};
Expand Down
52 changes: 46 additions & 6 deletions test/SILGen/enum_raw_representable_available.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.52

// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil -o %t.fragile.sil %s
// RUN: %FileCheck %s < %t.fragile.sil
// RUN: %FileCheck -check-prefix NEGATIVE %s < %t.fragile.sil
// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil %s -o %t.fragile.sil
// RUN: %FileCheck -check-prefixes=CHECK,CHECK-NO-EXTENSION %s < %t.fragile.sil
// RUN: %FileCheck -check-prefixes=NEGATIVE,NEGATIVE-NO-EXTENSION %s < %t.fragile.sil

// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil -enable-library-evolution -o %t.resilient.sil %s
// RUN: %FileCheck %s < %t.resilient.sil
// RUN: %FileCheck -check-prefix NEGATIVE %s < %t.resilient.sil
// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil %s -o %t.extensions-fragile.sil -application-extension
// RUN: %FileCheck -check-prefixes=CHECK,CHECK-EXTENSION %s < %t.extensions-fragile.sil
// RUN: %FileCheck -check-prefixes=NEGATIVE,NEGATIVE-EXTENSION %s < %t.extensions-fragile.sil

// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil %s -enable-library-evolution -o %t.resilient.sil
// RUN: %FileCheck -check-prefixes=CHECK,CHECK-NO-EXTENSION %s < %t.resilient.sil
// RUN: %FileCheck -check-prefixes=NEGATIVE,NEGATIVE-NO-EXTENSION %s < %t.resilient.sil

// RUN: %target-swift-emit-silgen -target %target-cpu-apple-macosx10.52 -emit-sorted-sil %s -enable-library-evolution -o %t.extensions-resilient.sil -application-extension
// RUN: %FileCheck -check-prefixes=CHECK,CHECK-EXTENSION %s < %t.extensions-resilient.sil
// RUN: %FileCheck -check-prefixes=NEGATIVE,NEGATIVE-EXTENSION %s < %t.extensions-resilient.sil

// This test just requires a platform with meaningful #available() checks, but
// for simplicity, it's written for macOS only.
Expand All @@ -25,9 +33,15 @@ public enum E: Int {
@available(macOS 10.55, *)
case potentiallyUnavailable = -3000

@available(macOSApplicationExtension 10.56, *)
case potentiallyUnavailableForExtensions = -3001

@available(macOS, unavailable)
case neverAvailable = -4000

@available(macOSApplicationExtension, unavailable)
case neverAvailableForExtensions = -4001

@available(macOS, obsoleted: 10.99)
case notObsoleteYet = -5000

Expand All @@ -46,13 +60,36 @@ public enum E: Int {
// CHECK: integer_literal $Builtin.IntLiteral, -3000
// CHECK: cond_br {{[^,]+}}, [[potentiallyUnavailable:bb[0-9]+]]

// CHECK: integer_literal $Builtin.IntLiteral, -3001
// CHECK: cond_br {{[^,]+}}, [[potentiallyUnavailableForExtensions:bb[0-9]+]]

// CHECK-NO-EXTENSION: integer_literal $Builtin.IntLiteral, -4001
// CHECK-NO-EXTENSION: cond_br {{[^,]+}}, [[neverAvailableForExtensions:bb[0-9]+]]

// CHECK: integer_literal $Builtin.IntLiteral, -5000
// CHECK: cond_br {{[^,]+}}, [[notObsoleteYet:bb[0-9]+]]

// CHECK: [[notObsoleteYet]]:
// CHECK-NOT: function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF
// CHECK: {{enum \$E|inject_enum_addr %[0-9]+ : \$\*E}}, #E.notObsoleteYet!enumelt

// CHECK-NO-EXTENSION: [[neverAvailableForExtensions]]:
// CHECK-NO-EXTENSION-NOT: function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF
// CHECK-NO-EXTENSION: {{enum \$E|inject_enum_addr %[0-9]+ : \$\*E}}, #E.neverAvailableForExtensions!enumelt

// CHECK: [[potentiallyUnavailableForExtensions]]:
// CHECK-NO-EXTENSION-NOT: function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF
// CHECK-NO-EXTENSION: {{enum \$E|inject_enum_addr %[0-9]+ : \$\*E}}, #E.potentiallyUnavailableForExtensions!enumelt
// CHECK-EXTENSION-NEXT: extend_lifetime
// CHECK-EXTENSION-NEXT: integer_literal $Builtin.Word, 10
// CHECK-EXTENSION-NEXT: integer_literal $Builtin.Word, 56
// CHECK-EXTENSION-NEXT: integer_literal $Builtin.Word, 0
// CHECK-EXTENSION: function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF
// CHECK-EXTENSION: cond_br {{[^,]+}}, [[potentiallyUnavailableForExtensions_newEnough:bb[0-9]+]],

// CHECK-EXTENSION: [[potentiallyUnavailableForExtensions_newEnough]]:
// CHECK-EXTENSION: {{enum \$E|inject_enum_addr %[0-9]+ : \$\*E}}, #E.potentiallyUnavailableForExtensions!enumelt

// CHECK: [[potentiallyUnavailable]]:
// CHECK-NEXT: extend_lifetime
// CHECK-NEXT: integer_literal $Builtin.Word, 10
Expand Down Expand Up @@ -84,6 +121,9 @@ public enum E: Int {
// Should not try to match neverAvailable's raw value
// NEGATIVE-NOT: integer_literal $Builtin.IntLiteral, -4000

// When building with -application-extension, should not try to match neverAvailableForExtensions's raw value
// NEGATIVE-EXTENSION-NOT: integer_literal $Builtin.IntLiteral, -4001

// Should not try to match nowObsolete's raw value
// NEGATIVE-NOT: integer_literal $Builtin.IntLiteral, -6000

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// RUN: cp -r %test-resource-dir/xros/Swift.swiftmodule %t/mock-sdk/usr/lib/swift/Swift.swiftmodule
// RUN: cp %S/Inputs/mock-visionos-sdk/SDKSettings.json %t/mock-sdk/SDKSettings.json
// RUN: %swift -emit-sil -parse-as-library %s -target arm64-apple-xros1.0 -sdk %t/mock-sdk -I %t/mock-sdk/usr/lib/swift/ -verify
// RUN: %swift -emit-silgen -parse-as-library %s -target arm64-apple-xros1.0 -sdk %t/mock-sdk -I %t/mock-sdk/usr/lib/swift/ -o %t/ios_available_rawvalue_enum_on_visionos.sil
// RUN: %FileCheck %s < %t/ios_available_rawvalue_enum_on_visionos.sil
// RUN: %swift -emit-silgen -parse-as-library %s -target arm64-apple-xros1.0 -sdk %t/mock-sdk -I %t/mock-sdk/usr/lib/swift/ -o %t/output.sil
// RUN: %FileCheck %s < %t/output.sil

// REQUIRES: OS=xros

Expand Down
Loading