Skip to content

AST/Sema: Introduce SemanticAvailabilitySpec #79566

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 4 commits into from
Feb 23, 2025
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: 2 additions & 2 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,9 @@ SWIFT_NAME("getter:BridgedAvailabilitySpec.platform(self:)")
BridgedPlatformKind
BridgedAvailabilitySpec_getPlatform(BridgedAvailabilitySpec spec);

SWIFT_NAME("getter:BridgedAvailabilitySpec.version(self:)")
SWIFT_NAME("getter:BridgedAvailabilitySpec.rawVersion(self:)")
BridgedVersionTuple
BridgedAvailabilitySpec_getVersion(BridgedAvailabilitySpec spec);
BridgedAvailabilitySpec_getRawVersion(BridgedAvailabilitySpec spec);

SWIFT_NAME("getter:BridgedAvailabilitySpec.versionRange(self:)")
BridgedSourceRange
Expand Down
28 changes: 28 additions & 0 deletions include/swift/AST/AvailabilityDomain.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,23 @@ class CustomAvailabilityDomain : public ASTAllocated<CustomAvailabilityDomain> {
/// Represents either a resolved availability domain or an identifier written
/// in source that has not yet been resolved to a domain.
class AvailabilityDomainOrIdentifier {
friend struct llvm::PointerLikeTypeTraits<AvailabilityDomainOrIdentifier>;

using Storage = llvm::PointerUnion<AvailabilityDomain, Identifier>;
Storage storage;

AvailabilityDomainOrIdentifier(Storage storage) : storage(storage) {}

public:
AvailabilityDomainOrIdentifier(Identifier identifier)
: storage(identifier) {};
AvailabilityDomainOrIdentifier(AvailabilityDomain domain)
: storage(domain) {};

static AvailabilityDomainOrIdentifier fromOpaque(void *opaque) {
return AvailabilityDomainOrIdentifier(Storage::getFromOpaqueValue(opaque));
}

bool isDomain() const { return storage.is<AvailabilityDomain>(); }
bool isIdentifier() const { return storage.is<Identifier>(); }

Expand All @@ -315,6 +323,7 @@ class AvailabilityDomainOrIdentifier {

namespace llvm {
using swift::AvailabilityDomain;
using swift::AvailabilityDomainOrIdentifier;

// An AvailabilityDomain is "pointer like".
template <typename T>
Expand Down Expand Up @@ -352,6 +361,25 @@ struct DenseMapInfo<AvailabilityDomain> {
}
};

// An AvailabilityDomainOrIdentifier is "pointer like".
template <typename T>
struct PointerLikeTypeTraits;
template <>
struct PointerLikeTypeTraits<swift::AvailabilityDomainOrIdentifier> {
public:
static inline void *getAsVoidPointer(AvailabilityDomainOrIdentifier value) {
return value.storage.getOpaqueValue();
}
static inline swift::AvailabilityDomainOrIdentifier
getFromVoidPointer(void *P) {
return AvailabilityDomainOrIdentifier::fromOpaque(P);
}
enum {
NumLowBitsAvailable = PointerLikeTypeTraits<
AvailabilityDomainOrIdentifier::Storage>::NumLowBitsAvailable
};
};

} // end namespace llvm

#endif
6 changes: 2 additions & 4 deletions include/swift/AST/AvailabilityScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,8 @@ class AvailabilityScope : public ASTAllocated<AvailabilityScope> {
/// condition that introduced the availability scope for a given platform
/// version; if zero or multiple such responsible attributes or statements
/// exist, returns an invalid SourceRange.
SourceRange
getAvailabilityConditionVersionSourceRange(
PlatformKind Platform,
const llvm::VersionTuple &Version) const;
SourceRange getAvailabilityConditionVersionSourceRange(
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const;

/// Returns the availability version range that was explicitly written in
/// source, if applicable. Otherwise, returns null.
Expand Down
83 changes: 75 additions & 8 deletions include/swift/AST/AvailabilitySpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
#include "swift/AST/AvailabilityDomain.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/PlatformKind.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/SourceLoc.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/VersionTuple.h"

namespace swift {
Expand All @@ -45,6 +47,8 @@ enum class AvailabilitySpecKind {
PackageDescriptionVersionConstraint,
};

class SemanticAvailabilitySpec;

/// The root class for specifications of API availability in availability
/// queries.
class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
Expand Down Expand Up @@ -109,7 +113,14 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {

AvailabilitySpecKind getKind() const { return Kind; }

bool isWildcard() { return getKind() == AvailabilitySpecKind::Wildcard; }
bool isWildcard() const {
return getKind() == AvailabilitySpecKind::Wildcard;
}

/// Returns a type-checked representation of the spec, or `std::nullopt` if
/// the spec is invalid.
std::optional<SemanticAvailabilitySpec>
getSemanticAvailabilitySpec(const DeclContext *declContext) const;

SourceRange getSourceRange() const { return SrcRange; }
SourceLoc getStartLoc() const { return SrcRange.Start; }
Expand All @@ -122,13 +133,8 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
return PlatformKind::none;
}

// The platform version to compare against.
llvm::VersionTuple getVersion() const;

// The version to be used in codegen for version comparisons at run time.
// This is required to support beta versions of macOS Big Sur that
// report 10.16 at run time.
llvm::VersionTuple getRuntimeVersion() const { return Version; }
// The version tuple that was written in source.
llvm::VersionTuple getRawVersion() const { return Version; }

SourceRange getVersionSrcRange() const {
if (!VersionStartLoc)
Expand All @@ -141,6 +147,67 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
void setMacroLoc(SourceLoc loc) { MacroLoc = loc; }
};

/// The type-checked representation of `AvailabilitySpec` which guaranatees that
/// the spec has a valid `AvailabilityDomain`.
class SemanticAvailabilitySpec {
const AvailabilitySpec *spec;

public:
SemanticAvailabilitySpec(const AvailabilitySpec *spec) : spec(spec) {
// The domain must be resolved in order to wrap it in a semantic spec.
ASSERT(spec->isWildcard() || spec->getDomain());
}

const AvailabilitySpec *getParsedSpec() const { return spec; }

AvailabilityDomain getDomain() const {
if (isWildcard())
return AvailabilityDomain::forUniversal();
return spec->getDomain().value();
}

bool isWildcard() const { return spec->isWildcard(); }

// The platform version to compare against.
llvm::VersionTuple getVersion() const;

// The version to be used in codegen for version comparisons at run time.
// This is required to support beta versions of macOS Big Sur that
// report 10.16 at run time.
llvm::VersionTuple getRuntimeVersion() const { return spec->getRawVersion(); }
};

/// Wraps an array of availability specs and provides an iterator for their
/// semantic representations.
class SemanticAvailabilitySpecs {
public:
class Filter final {
const DeclContext *declContext;

public:
Filter(const DeclContext *declContext) : declContext(declContext) {}

std::optional<SemanticAvailabilitySpec>
operator()(const AvailabilitySpec *spec) const;
};

using Range = OptionalTransformRange<
iterator_range<ArrayRef<AvailabilitySpec *>::const_iterator>, Filter>;

private:
Range specRange;

public:
SemanticAvailabilitySpecs(ArrayRef<AvailabilitySpec *> specs,
const DeclContext *declContext)
: specRange(llvm::make_range(specs.begin(), specs.end()),
Filter(declContext)) {}

Range::iterator begin() const { return specRange.begin(); }
Range::iterator end() const { return specRange.end(); }
bool empty() const { return specRange.empty(); }
};

/// Maps of macro name and version to availability specifications.
/// Organized as two nested \c DenseMap keyed first on the macro name then
/// the macro version. This structure allows to peek at macro names before
Expand Down
7 changes: 6 additions & 1 deletion include/swift/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class FuncDecl;
class AbstractFunctionDecl;
class Pattern;
class PatternBindingDecl;
class SemanticAvailabilitySpecs;
class VarDecl;
class CaseStmt;
class DoCatchStmt;
Expand Down Expand Up @@ -516,7 +517,11 @@ class alignas(8) PoundAvailableInfo final :
ArrayRef<AvailabilitySpec *> getQueries() const {
return llvm::ArrayRef(getTrailingObjects<AvailabilitySpec *>(), NumQueries);
}


/// Returns an iterator for the statement's type-checked availability specs.
SemanticAvailabilitySpecs
getSemanticAvailabilitySpecs(const DeclContext *declContext) const;

SourceLoc getLParenLoc() const { return LParenLoc; }
SourceLoc getRParenLoc() const { return RParenLoc; }

Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1108,9 +1108,9 @@ namespace {
? "*"
: Spec->getDomain()->getNameForAttributePrinting();
printField(domainName, Label::always("domain"));
if (!Spec->getVersion().empty())
if (!Spec->getRawVersion().empty())
printFieldRaw(
[&](llvm::raw_ostream &OS) { OS << Spec->getVersion(); },
[&](llvm::raw_ostream &OS) { OS << Spec->getRawVersion(); },
Label::always("version"));
printFoot();
},
Expand Down
38 changes: 20 additions & 18 deletions lib/AST/AvailabilityScope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,31 +265,31 @@ SourceLoc AvailabilityScope::getIntroductionLoc() const {
llvm_unreachable("Unhandled Reason in switch.");
}

static SourceRange
getAvailabilityConditionVersionSourceRange(const PoundAvailableInfo *PAI,
PlatformKind Platform,
const llvm::VersionTuple &Version) {
static SourceRange getAvailabilityConditionVersionSourceRange(
const PoundAvailableInfo *PAI, const DeclContext *ReferenceDC,
AvailabilityDomain Domain, const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto *Spec : PAI->getQueries()) {
if (Spec->getPlatform() == Platform && Spec->getVersion() == Version) {
for (auto Spec : PAI->getSemanticAvailabilitySpecs(ReferenceDC)) {
if (Spec.getDomain() == Domain && Spec.getVersion() == Version) {
// More than one: return invalid range, no unique choice.
if (Range.isValid())
return SourceRange();
else
Range = Spec->getVersionSrcRange();
Range = Spec.getParsedSpec()->getVersionSrcRange();
}
}
return Range;
}

static SourceRange getAvailabilityConditionVersionSourceRange(
const MutableArrayRef<StmtConditionElement> &Conds, PlatformKind Platform,
const MutableArrayRef<StmtConditionElement> &Conds,
const DeclContext *ReferenceDC, AvailabilityDomain Domain,
const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto const &C : Conds) {
if (C.getKind() == StmtConditionElement::CK_Availability) {
SourceRange R = getAvailabilityConditionVersionSourceRange(
C.getAvailability(), Platform, Version);
C.getAvailability(), ReferenceDC, Domain, Version);
// More than one: return invalid range.
if (Range.isValid())
return SourceRange();
Expand All @@ -301,13 +301,13 @@ static SourceRange getAvailabilityConditionVersionSourceRange(
}

static SourceRange
getAvailabilityConditionVersionSourceRange(const Decl *D, PlatformKind Platform,
getAvailabilityConditionVersionSourceRange(const Decl *D,
AvailabilityDomain Domain,
const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto Attr : D->getSemanticAvailableAttrs()) {
if (Attr.getIntroduced().has_value() &&
Attr.getIntroduced().value() == Version &&
Attr.getPlatform() == Platform) {
Attr.getIntroduced().value() == Version && Attr.getDomain() == Domain) {

// More than one: return invalid range.
if (Range.isValid())
Expand All @@ -320,29 +320,31 @@ getAvailabilityConditionVersionSourceRange(const Decl *D, PlatformKind Platform,
}

SourceRange AvailabilityScope::getAvailabilityConditionVersionSourceRange(
PlatformKind Platform, const llvm::VersionTuple &Version) const {
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const {
switch (getReason()) {
case Reason::Decl:
return ::getAvailabilityConditionVersionSourceRange(Node.getAsDecl(),
Platform, Version);
Domain, Version);

case Reason::IfStmtThenBranch:
case Reason::IfStmtElseBranch:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsIfStmt()->getCond(), Platform, Version);
Node.getAsIfStmt()->getCond(), Node.getDeclContext(), Domain, Version);

case Reason::ConditionFollowingAvailabilityQuery:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsPoundAvailableInfo(), Platform, Version);
Node.getAsPoundAvailableInfo(), Node.getDeclContext(), Domain, Version);

case Reason::GuardStmtFallthrough:
case Reason::GuardStmtElseBranch:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsGuardStmt()->getCond(), Platform, Version);
Node.getAsGuardStmt()->getCond(), Node.getDeclContext(), Domain,
Version);

case Reason::WhileStmtBody:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsWhileStmt()->getCond(), Platform, Version);
Node.getAsWhileStmt()->getCond(), Node.getDeclContext(), Domain,
Version);

case Reason::Root:
case Reason::DeclImplicit:
Expand Down
21 changes: 19 additions & 2 deletions lib/AST/AvailabilitySpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ AvailabilitySpec *AvailabilitySpec::createPlatformVersioned(
SourceRange(platformLoc, versionRange.End), version, versionRange.Start);
}

llvm::VersionTuple AvailabilitySpec::getVersion() const {
llvm::VersionTuple SemanticAvailabilitySpec::getVersion() const {
// For macOS Big Sur, we canonicalize 10.16 to 11.0 for compile-time
// checking since clang canonicalizes availability markup. However, to
// support Beta versions of macOS Big Sur where the OS
Expand All @@ -77,5 +77,22 @@ llvm::VersionTuple AvailabilitySpec::getVersion() const {
//
// we need to store the uncanonicalized version for codegen and canonicalize
// it as necessary for compile-time checks.
return canonicalizePlatformVersion(getPlatform(), Version);
return canonicalizePlatformVersion(getDomain().getPlatformKind(),
spec->getRawVersion());
}

std::optional<SemanticAvailabilitySpec>
AvailabilitySpec::getSemanticAvailabilitySpec(
const DeclContext *declContext) const {
if (isWildcard() || getDomain())
return SemanticAvailabilitySpec(this);
return std::nullopt;
}

std::optional<SemanticAvailabilitySpec>
SemanticAvailabilitySpecs::Filter::operator()(
const AvailabilitySpec *spec) const {
if (auto semanticSpec = spec->getSemanticAvailabilitySpec(declContext))
return semanticSpec;
return std::nullopt;
}
4 changes: 2 additions & 2 deletions lib/AST/Bridging/AvailabilityBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ BridgedAvailabilitySpec_getPlatform(BridgedAvailabilitySpec spec) {
}

BridgedVersionTuple
BridgedAvailabilitySpec_getVersion(BridgedAvailabilitySpec spec) {
return spec.unbridged()->getVersion();
BridgedAvailabilitySpec_getRawVersion(BridgedAvailabilitySpec spec) {
return spec.unbridged()->getRawVersion();
}

BridgedSourceRange
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/Stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,11 @@ PoundAvailableInfo::create(ASTContext &ctx, SourceLoc PoundLoc,
RParenLoc, isUnavailability);
}

SemanticAvailabilitySpecs PoundAvailableInfo::getSemanticAvailabilitySpecs(
const DeclContext *declContext) const {
return SemanticAvailabilitySpecs(getQueries(), declContext);
}

SourceLoc PoundAvailableInfo::getEndLoc() const {
if (RParenLoc.isInvalid()) {
if (NumQueries == 0) {
Expand Down
Loading