Skip to content

Commit dd68e98

Browse files
authored
Merge pull request #80051 from tshortli/refactor-semantic-decl-availability-request
AST: Introduce `DeclRuntimeAvailability`
2 parents 90340a0 + 7ed2a0d commit dd68e98

13 files changed

+212
-133
lines changed

include/swift/AST/AvailabilityConstraint.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ enum class AvailabilityConstraintFlag : uint8_t {
170170
/// referencing the extension. When this flag is specified, though, only the
171171
/// attributes directly attached to the declaration are considered.
172172
SkipEnclosingExtension = 1 << 0,
173+
174+
/// Include constraints for all domains, regardless of whether they are active
175+
/// or relevant to type checking.
176+
IncludeAllDomains = 1 << 1,
173177
};
174178
using AvailabilityConstraintFlags = OptionSet<AvailabilityConstraintFlag>;
175179

include/swift/AST/Decl.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,11 +1475,6 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
14751475
/// extension) that makes it unavailable.
14761476
std::optional<SemanticAvailableAttr> getUnavailableAttr() const;
14771477

1478-
/// Returns true if the decl is effectively always unavailable in the current
1479-
/// compilation context. This query differs from \c isUnavailable() because it
1480-
/// takes the availability of parent declarations into account.
1481-
bool isSemanticallyUnavailable() const;
1482-
14831478
/// Returns true if code associated with this declaration should be considerd
14841479
/// unreachable at runtime because the declaration is unavailable in all
14851480
/// execution contexts in which the code may run. This result takes the

include/swift/AST/TypeCheckRequests.h

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,35 +4128,45 @@ class RenamedDeclRequest
41284128
void cacheResult(ValueDecl *value) const;
41294129
};
41304130

4131-
enum class SemanticDeclAvailability : uint8_t {
4132-
/// The decl is potentially available in some contexts and/or under certain
4133-
/// deployment conditions.
4131+
/// Describes the runtime availability of a declaration, which is a
4132+
/// classification of whether a decl can be used at runtime (as opposed to
4133+
/// compile time).
4134+
///
4135+
/// The elements of this enumeration must be ordered from most available to
4136+
/// least available.
4137+
enum class DeclRuntimeAvailability : uint8_t {
4138+
/// The decl is potentially available at runtime. If it is unavailable at
4139+
/// compile time in the current module, it may still be considered available
4140+
/// at compile time by other modules with different settings. For example, a
4141+
/// decl that is obsolete in Swift 5 is still available to other modules that
4142+
/// are compiled for an earlier language mode.
41344143
PotentiallyAvailable,
41354144

4136-
/// The decl is always unavailable in the current compilation context.
4137-
/// However, it may still be used at runtime by other modules with different
4138-
/// settings. For example a decl that is obsolete in Swift 5 is still
4139-
/// available to other modules compiled for an earlier language mode.
4140-
ConditionallyUnavailable,
4141-
4142-
/// The decl is universally unavailable. For example, when compiling for macOS
4143-
/// a decl with `@available(macOS, unavailable)` can never be used (except in
4144-
/// contexts that are also completely unavailable on macOS).
4145-
CompletelyUnavailable,
4146-
};
4147-
4148-
class SemanticDeclAvailabilityRequest
4149-
: public SimpleRequest<SemanticDeclAvailabilityRequest,
4150-
SemanticDeclAvailability(const Decl *decl),
4145+
/// The decl is always unavailable at compile time in the current module and
4146+
/// all other modules, but it is still required to be present at load time to
4147+
/// maintain ABI compatibility. For example, when compiling for macOS a decl
4148+
/// with an `@available(macOS, unavailable)` attribute can never be invoked,
4149+
/// except in contexts that are also completely unavailable on macOS. This
4150+
/// means the declaration is unreachable by execution at runtime, but the
4151+
/// decl's symbols may still have been strongly linked by other binaries built
4152+
/// by older versions of the compiler which may have emitted unavailable code
4153+
/// with strong references. To preserve ABI stability, the decl must still be
4154+
/// emitted.
4155+
AlwaysUnavailableABICompatible,
4156+
};
4157+
4158+
class DeclRuntimeAvailabilityRequest
4159+
: public SimpleRequest<DeclRuntimeAvailabilityRequest,
4160+
DeclRuntimeAvailability(const Decl *decl),
41514161
RequestFlags::Cached> {
41524162
public:
41534163
using SimpleRequest::SimpleRequest;
41544164

41554165
private:
41564166
friend SimpleRequest;
41574167

4158-
SemanticDeclAvailability evaluate(Evaluator &evaluator,
4159-
const Decl *decl) const;
4168+
DeclRuntimeAvailability evaluate(Evaluator &evaluator,
4169+
const Decl *decl) const;
41604170

41614171
public:
41624172
bool isCached() const { return true; }

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,8 @@ SWIFT_REQUEST(TypeChecker, ImplicitKnownProtocolConformanceRequest,
473473
SWIFT_REQUEST(TypeChecker, RenamedDeclRequest,
474474
ValueDecl *(const ValueDecl *, const AvailableAttr *),
475475
Cached, NoLocationInfo)
476-
SWIFT_REQUEST(TypeChecker, SemanticDeclAvailabilityRequest,
477-
SemanticDeclAvailability(const Decl *),
476+
SWIFT_REQUEST(TypeChecker, DeclRuntimeAvailabilityRequest,
477+
DeclRuntimeAvailability(const Decl *),
478478
Cached, NoLocationInfo)
479479
SWIFT_REQUEST(TypeChecker, ClosureEffectsRequest,
480480
FunctionType::ExtInfo(ClosureExpr *),

lib/AST/Availability.cpp

Lines changed: 74 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -541,122 +541,120 @@ std::optional<SemanticAvailableAttr> Decl::getUnavailableAttr() const {
541541
return std::nullopt;
542542
}
543543

544-
static llvm::SmallVector<AvailabilityDomain, 2>
544+
static llvm::SmallSetVector<AvailabilityDomain, 2>
545545
availabilityDomainsForABICompatibility(const ASTContext &ctx) {
546-
llvm::SmallVector<AvailabilityDomain, 2> domains;
546+
llvm::SmallSetVector<AvailabilityDomain, 2> domains;
547547

548548
// Regardless of target platform, binaries built for Embedded do not require
549549
// compatibility.
550550
if (ctx.LangOpts.hasFeature(Feature::Embedded))
551551
return domains;
552552

553553
if (auto targetDomain = AvailabilityDomain::forTargetPlatform(ctx))
554-
domains.push_back(targetDomain->getABICompatibilityDomain());
555-
554+
domains.insert(targetDomain->getABICompatibilityDomain());
555+
556556
if (auto variantDomain = AvailabilityDomain::forTargetVariantPlatform(ctx))
557-
domains.push_back(variantDomain->getABICompatibilityDomain());
557+
domains.insert(variantDomain->getABICompatibilityDomain());
558558

559559
return domains;
560560
}
561561

562-
/// Returns true if \p decl is proven to be unavailable for all platforms that
563-
/// external modules interacting with this module could target. A declaration
564-
/// that is not proven to be unavailable in this way could be reachable at
565-
/// runtime, even if it is unavailable to all code in this module.
566-
static bool isUnavailableForAllABICompatiblePlatforms(const Decl *decl) {
562+
static bool constraintIndicatesRuntimeUnavailability(
563+
const AvailabilityConstraint &constraint, const ASTContext &ctx) {
564+
switch (constraint.getReason()) {
565+
case AvailabilityConstraint::Reason::UnconditionallyUnavailable: {
566+
return true;
567+
}
568+
case AvailabilityConstraint::Reason::UnavailableForDeployment:
569+
case AvailabilityConstraint::Reason::Obsoleted:
570+
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
571+
return false;
572+
}
573+
}
574+
575+
/// Computes the `DeclRuntimeAvailability` value for `decl`.
576+
static DeclRuntimeAvailability getDeclRuntimeAvailability(const Decl *decl) {
567577
// Don't trust unavailability on declarations from Clang modules.
568578
if (isa<ClangModuleUnit>(decl->getDeclContext()->getModuleScopeContext()))
569-
return false;
579+
return DeclRuntimeAvailability::PotentiallyAvailable;
580+
581+
// Check whether the decl is unavailable at all.
582+
if (!decl->isUnavailable())
583+
return DeclRuntimeAvailability::PotentiallyAvailable;
570584

571585
auto &ctx = decl->getASTContext();
572-
llvm::SmallVector<AvailabilityDomain, 2> compatibilityDomains =
573-
availabilityDomainsForABICompatibility(ctx);
574-
575-
llvm::SmallSet<AvailabilityDomain, 8> unavailableDescendantDomains;
576-
llvm::SmallSet<AvailabilityDomain, 8> availableDescendantDomains;
577-
578-
// Build up the collection of relevant available and unavailable platform
579-
// domains by looking at all the @available attributes. Along the way, we
580-
// may find an attribute that makes the declaration universally unavailable
581-
// in which case platform availability is irrelevant.
582-
for (auto attr : decl->getSemanticAvailableAttrs(/*includeInactive=*/true)) {
583-
auto domain = attr.getDomain();
586+
auto compatibilityDomains = availabilityDomainsForABICompatibility(ctx);
587+
auto potentiallyAvailableDomains = compatibilityDomains;
588+
589+
AvailabilityConstraintFlags flags;
590+
591+
// Semantic availability was already computed separately for any enclosing
592+
// extension.
593+
flags |= AvailabilityConstraintFlag::SkipEnclosingExtension;
594+
595+
// FIXME: [availability] Inactive domains have to be included because iOS
596+
// availability is considered inactive when compiling a zippered module.
597+
flags |= AvailabilityConstraintFlag::IncludeAllDomains;
598+
599+
auto constraints = getAvailabilityConstraintsForDecl(
600+
decl, AvailabilityContext::forInliningTarget(ctx), flags);
601+
602+
for (auto constraint : constraints) {
603+
if (!constraintIndicatesRuntimeUnavailability(constraint, ctx))
604+
continue;
605+
606+
// Check whether the constraint is from a relevant domain.
607+
auto domain = constraint.getDomain();
584608
bool isCompabilityDomainDescendant =
585609
llvm::find_if(compatibilityDomains,
586610
[&domain](AvailabilityDomain compatibilityDomain) {
587611
return compatibilityDomain.contains(domain);
588612
}) != compatibilityDomains.end();
589613

614+
if (!domain.isActive(ctx) && !isCompabilityDomainDescendant)
615+
continue;
616+
590617
if (isCompabilityDomainDescendant) {
591-
// Record the whether the descendant domain is marked available
592-
// or unavailable. Unavailability overrides availability.
593-
if (attr.isUnconditionallyUnavailable()) {
594-
availableDescendantDomains.erase(domain);
595-
unavailableDescendantDomains.insert(domain);
596-
} else if (!unavailableDescendantDomains.contains(domain)) {
597-
availableDescendantDomains.insert(domain);
598-
}
599-
} else if (attr.isActive(ctx)) {
600-
// The declaration is always unavailable if an active attribute from a
601-
// domain outside the compatibility hierarchy indicates unavailability.
602-
if (attr.isUnconditionallyUnavailable())
603-
return true;
618+
// If the decl is still potentially available in some compatibility
619+
// domain, keep looking at the remaining constraints.
620+
potentiallyAvailableDomains.remove(domain);
621+
if (!potentiallyAvailableDomains.empty())
622+
continue;
604623
}
605-
}
606624

607-
// If there aren't any compatibility domains to check and we didn't find any
608-
// other active attributes that make the declaration unavailable, then it must
609-
// be available.
610-
if (compatibilityDomains.empty())
611-
return false;
612-
613-
// Verify that the declaration has been marked unavailable in every
614-
// compatibility domain.
615-
for (auto compatibilityDomain : compatibilityDomains) {
616-
if (!unavailableDescendantDomains.contains(compatibilityDomain))
617-
return false;
625+
// Either every compatibility domain has been proven unavailable or this
626+
// constraint proves runtime unavailability on its own.
627+
return DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
618628
}
619-
620-
// Verify that there aren't any explicitly available descendant domains.
621-
if (availableDescendantDomains.size() > 0)
622-
return false;
623629

624-
return true;
630+
return DeclRuntimeAvailability::PotentiallyAvailable;
625631
}
626632

627-
SemanticDeclAvailability
628-
SemanticDeclAvailabilityRequest::evaluate(Evaluator &evaluator,
629-
const Decl *decl) const {
630-
auto inherited = SemanticDeclAvailability::PotentiallyAvailable;
633+
DeclRuntimeAvailability
634+
DeclRuntimeAvailabilityRequest::evaluate(Evaluator &evaluator,
635+
const Decl *decl) const {
636+
auto inherited = DeclRuntimeAvailability::PotentiallyAvailable;
631637
if (auto *parent =
632638
AvailabilityInference::parentDeclForInferredAvailability(decl)) {
633639
inherited = evaluateOrDefault(
634-
evaluator, SemanticDeclAvailabilityRequest{parent}, inherited);
640+
evaluator, DeclRuntimeAvailabilityRequest{parent}, inherited);
635641
}
636642

637-
if (inherited == SemanticDeclAvailability::CompletelyUnavailable ||
638-
isUnavailableForAllABICompatiblePlatforms(decl))
639-
return SemanticDeclAvailability::CompletelyUnavailable;
640-
641-
if (inherited == SemanticDeclAvailability::ConditionallyUnavailable ||
642-
decl->isUnavailable())
643-
return SemanticDeclAvailability::ConditionallyUnavailable;
644-
645-
return SemanticDeclAvailability::PotentiallyAvailable;
646-
}
643+
// If the inherited semantic availability is already maximally unavailable
644+
// then skip computing unavailability for this declaration.
645+
if (inherited == DeclRuntimeAvailability::AlwaysUnavailableABICompatible)
646+
return DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
647647

648-
bool Decl::isSemanticallyUnavailable() const {
649-
auto availability = evaluateOrDefault(
650-
getASTContext().evaluator, SemanticDeclAvailabilityRequest{this},
651-
SemanticDeclAvailability::PotentiallyAvailable);
652-
return availability != SemanticDeclAvailability::PotentiallyAvailable;
648+
auto availability = getDeclRuntimeAvailability(decl);
649+
return std::max(inherited, availability);
653650
}
654651

655652
bool Decl::isUnreachableAtRuntime() const {
656653
auto availability = evaluateOrDefault(
657-
getASTContext().evaluator, SemanticDeclAvailabilityRequest{this},
658-
SemanticDeclAvailability::PotentiallyAvailable);
659-
return availability == SemanticDeclAvailability::CompletelyUnavailable;
654+
getASTContext().evaluator, DeclRuntimeAvailabilityRequest{this},
655+
DeclRuntimeAvailability::PotentiallyAvailable);
656+
return availability ==
657+
DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
660658
}
661659

662660
static UnavailableDeclOptimization
@@ -859,8 +857,6 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getIntroduced() const {
859857

860858
std::optional<AvailabilityRange>
861859
SemanticAvailableAttr::getIntroducedRange(const ASTContext &Ctx) const {
862-
DEBUG_ASSERT(getDomain().isActive(Ctx));
863-
864860
auto *attr = getParsedAttr();
865861
if (!attr->getRawIntroduced().has_value()) {
866862
// For versioned domains, an "introduced:" version is always required to
@@ -899,8 +895,6 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
899895

900896
std::optional<AvailabilityRange>
901897
SemanticAvailableAttr::getDeprecatedRange(const ASTContext &Ctx) const {
902-
DEBUG_ASSERT(getDomain().isActive(Ctx));
903-
904898
auto *attr = getParsedAttr();
905899
if (!attr->getRawDeprecated().has_value()) {
906900
// Regardless of the whether the domain supports versions or not, an
@@ -930,8 +924,6 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {
930924

931925
std::optional<AvailabilityRange>
932926
SemanticAvailableAttr::getObsoletedRange(const ASTContext &Ctx) const {
933-
DEBUG_ASSERT(getDomain().isActive(Ctx));
934-
935927
auto *attr = getParsedAttr();
936928

937929
// Obsoletion always requires a version.

lib/AST/AvailabilityConstraint.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,15 @@ activePlatformDomainForDecl(const Decl *decl) {
198198

199199
static void getAvailabilityConstraintsForDecl(
200200
llvm::SmallVector<AvailabilityConstraint, 4> &constraints, const Decl *decl,
201-
const AvailabilityContext &context) {
201+
const AvailabilityContext &context, AvailabilityConstraintFlags flags) {
202202
auto &ctx = decl->getASTContext();
203203
auto activePlatformDomain = activePlatformDomainForDecl(decl);
204+
bool includeAllDomains =
205+
flags.contains(AvailabilityConstraintFlag::IncludeAllDomains);
204206

205-
for (auto attr :
206-
decl->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
207+
for (auto attr : decl->getSemanticAvailableAttrs(includeAllDomains)) {
207208
auto domain = attr.getDomain();
208-
if (domain.isPlatform() && activePlatformDomain &&
209+
if (!includeAllDomains && domain.isPlatform() && activePlatformDomain &&
209210
!activePlatformDomain->contains(domain))
210211
continue;
211212

@@ -234,7 +235,7 @@ swift::getAvailabilityConstraintsForDecl(const Decl *decl,
234235

235236
decl = decl->getAbstractSyntaxDeclForAttributes();
236237

237-
getAvailabilityConstraintsForDecl(constraints, decl, context);
238+
getAvailabilityConstraintsForDecl(constraints, decl, context, flags);
238239

239240
if (flags.contains(AvailabilityConstraintFlag::SkipEnclosingExtension))
240241
return constraints;
@@ -251,7 +252,7 @@ swift::getAvailabilityConstraintsForDecl(const Decl *decl,
251252

252253
auto parent = AvailabilityInference::parentDeclForInferredAvailability(decl);
253254
if (auto extension = dyn_cast_or_null<ExtensionDecl>(parent))
254-
getAvailabilityConstraintsForDecl(constraints, extension, context);
255+
getAvailabilityConstraintsForDecl(constraints, extension, context, flags);
255256

256257
return constraints;
257258
}

lib/SIL/IR/SILProfiler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/SIL/SILProfiler.h"
1414
#include "swift/AST/ASTWalker.h"
15+
#include "swift/AST/AvailabilityContext.h"
1516
#include "swift/AST/Decl.h"
1617
#include "swift/AST/Expr.h"
1718
#include "swift/AST/Module.h"
@@ -117,7 +118,7 @@ static bool shouldProfile(SILDeclRef Constant) {
117118

118119
if (auto *D = DC->getInnermostDeclarationDeclContext()) {
119120
// Do not profile AST nodes in unavailable contexts.
120-
if (D->isSemanticallyUnavailable()) {
121+
if (AvailabilityContext::forDeclSignature(D).isUnavailable()) {
121122
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: unavailable context\n");
122123
return false;
123124
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5455,7 +5455,7 @@ TypeChecker::diagnosticIfDeclCannotBeUnavailable(const Decl *D,
54555455
auto parentIsUnavailable = [](const Decl *D) -> bool {
54565456
if (auto *parent =
54575457
AvailabilityInference::parentDeclForInferredAvailability(D)) {
5458-
return parent->isSemanticallyUnavailable();
5458+
return AvailabilityContext::forDeclSignature(parent).isUnavailable();
54595459
}
54605460
return false;
54615461
};

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1933,7 +1933,7 @@ checkOverrideUnavailability(ValueDecl *override, ValueDecl *base) {
19331933
if (auto *overrideParent = override->getDeclContext()->getAsDecl()) {
19341934
// If the parent of the override is unavailable, then the unavailability of
19351935
// the override decl is irrelevant.
1936-
if (overrideParent->isSemanticallyUnavailable())
1936+
if (AvailabilityContext::forDeclSignature(overrideParent).isUnavailable())
19371937
return {OverrideUnavailabilityStatus::Ignored, std::nullopt};
19381938
}
19391939

0 commit comments

Comments
 (0)