Skip to content

AST/Sema: Continue adopting AvailabilityConstraint #79260

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
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
23 changes: 0 additions & 23 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,23 +703,6 @@ class SwiftNativeObjCRuntimeBaseAttr : public DeclAttribute {
}
};

/// Determine the result of comparing an availability attribute to a specific
/// platform or language version.
enum class AvailableVersionComparison {
/// The entity is guaranteed to be available.
Available,

/// The entity is never available.
Unavailable,

/// The entity might be unavailable at runtime, because it was introduced
/// after the requested minimum platform version.
PotentiallyUnavailable,

/// The entity has been obsoleted.
Obsoleted,
};

/// Defines the @available attribute.
class AvailableAttr : public DeclAttribute {
public:
Expand Down Expand Up @@ -3378,12 +3361,6 @@ class SemanticAvailableAttr final {
/// version for PackageDescription version-specific availability.
llvm::VersionTuple getActiveVersion(const ASTContext &ctx) const;

/// Compare this attribute's version information against the platform or
/// language version (assuming the this attribute pertains to the active
/// platform).
AvailableVersionComparison
getVersionAvailability(const ASTContext &ctx) const;

/// Returns true if this attribute is considered active in the current
/// compilation context.
bool isActive(ASTContext &ctx) const;
Expand Down
41 changes: 26 additions & 15 deletions include/swift/AST/AvailabilityConstraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "swift/AST/AvailabilityRange.h"
#include "swift/AST/PlatformKind.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/OptionSet.h"

namespace swift {

Expand All @@ -33,6 +34,10 @@ class Decl;
/// certain context.
class AvailabilityConstraint {
public:
/// The reason that the availability constraint is unsatisfied.
///
/// NOTE: The order of this enum matters. Reasons are defined in descending
/// priority order.
enum class Reason {
/// The declaration is referenced in a context in which it is generally
/// unavailable. For example, a reference to a declaration that is
Expand Down Expand Up @@ -133,44 +138,50 @@ class AvailabilityConstraint {
/// Returns the required range for `IntroducedInNewerVersion` requirements, or
/// `std::nullopt` otherwise.
std::optional<AvailabilityRange>
getRequiredNewerAvailabilityRange(ASTContext &ctx) const;
getRequiredNewerAvailabilityRange(const ASTContext &ctx) 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;
bool isActiveForRuntimeQueries(const ASTContext &ctx) const;
};

/// Represents a set of availability constraints that restrict use of a
/// declaration in a particular context.
/// declaration in a particular context. There can only be one active constraint
/// for a given `AvailabilityDomain`, but there may be multiple active
/// constraints from separate domains.
class DeclAvailabilityConstraints {
using Storage = llvm::SmallVector<AvailabilityConstraint, 4>;
Storage constraints;

public:
DeclAvailabilityConstraints() {}
DeclAvailabilityConstraints(const Storage &&constraints)
: constraints(constraints) {}

void addConstraint(const AvailabilityConstraint &constraint) {
constraints.emplace_back(constraint);
}
/// Returns the strongest availability constraint or `std::nullopt` if empty.
std::optional<AvailabilityConstraint> getPrimaryConstraint() const;

using const_iterator = Storage::const_iterator;
const_iterator begin() const { return constraints.begin(); }
const_iterator end() const { return constraints.end(); }
};

/// Returns the `AvailabilityConstraint` that describes how \p attr restricts
/// use of \p decl in \p context or `std::nullopt` if there is no restriction.
std::optional<AvailabilityConstraint>
getAvailabilityConstraintForAttr(const Decl *decl,
const SemanticAvailableAttr &attr,
const AvailabilityContext &context);
enum class AvailabilityConstraintFlag : uint8_t {
/// By default, the availability constraints for the members of extensions
/// include the constraints for `@available` attributes that were written on
/// the enclosing extension, since these members can be referred to without
/// referencing the extension. When this flag is specified, though, only the
/// attributes directly attached to the declaration are considered.
SkipEnclosingExtension = 1 << 0,
};
using AvailabilityConstraintFlags = OptionSet<AvailabilityConstraintFlag>;

/// Returns the set of availability constraints that restrict use of \p decl
/// when it is referenced from the given context. In other words, it is the
/// collection of of `@available` attributes with unsatisfied conditions.
DeclAvailabilityConstraints
getAvailabilityConstraintsForDecl(const Decl *decl,
const AvailabilityContext &context);
DeclAvailabilityConstraints getAvailabilityConstraintsForDecl(
const Decl *decl, const AvailabilityContext &context,
AvailabilityConstraintFlags flags = std::nullopt);
} // end namespace swift

#endif
44 changes: 0 additions & 44 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2294,50 +2294,6 @@ SemanticAvailableAttr::getActiveVersion(const ASTContext &ctx) const {
}
}

AvailableVersionComparison
SemanticAvailableAttr::getVersionAvailability(const ASTContext &ctx) const {

// Unconditional unavailability.
if (attr->isUnconditionallyUnavailable())
return AvailableVersionComparison::Unavailable;

llvm::VersionTuple queryVersion = getActiveVersion(ctx);
std::optional<llvm::VersionTuple> ObsoletedVersion = getObsoleted();

StringRef ObsoletedPlatform;
llvm::VersionTuple RemappedObsoletedVersion;
if (AvailabilityInference::updateObsoletedPlatformForFallback(
*this, ctx, ObsoletedPlatform, RemappedObsoletedVersion))
ObsoletedVersion = RemappedObsoletedVersion;

// If this entity was obsoleted before or at the query platform version,
// consider it obsolete.
if (ObsoletedVersion && *ObsoletedVersion <= queryVersion)
return AvailableVersionComparison::Obsoleted;

std::optional<llvm::VersionTuple> IntroducedVersion = getIntroduced();
StringRef IntroducedPlatform;
llvm::VersionTuple RemappedIntroducedVersion;
if (AvailabilityInference::updateIntroducedPlatformForFallback(
*this, ctx, IntroducedPlatform, RemappedIntroducedVersion))
IntroducedVersion = RemappedIntroducedVersion;

// If this entity was introduced after the query version and we're doing a
// platform comparison, true availability can only be determined dynamically;
// if we're doing a _language_ version check, the query version is a
// static requirement, so we treat "introduced later" as just plain
// unavailable.
if (IntroducedVersion && *IntroducedVersion > queryVersion) {
if (isSwiftLanguageModeSpecific() || isPackageDescriptionVersionSpecific())
return AvailableVersionComparison::Unavailable;
else
return AvailableVersionComparison::PotentiallyUnavailable;
}

// The entity is available.
return AvailableVersionComparison::Available;
}

SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range,
TrailingWhereClause *clause, bool exported,
SpecializationKind kind,
Expand Down
48 changes: 6 additions & 42 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,49 +573,13 @@ bool Decl::isUnavailableInCurrentSwiftVersion() const {
return false;
}

std::optional<SemanticAvailableAttr> getDeclUnavailableAttr(const Decl *D) {
auto &ctx = D->getASTContext();
std::optional<SemanticAvailableAttr> result;
auto bestActive = D->getActiveAvailableAttrForCurrentPlatform();

for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
// If this is a platform-specific attribute and it isn't the most
// specific attribute for the current platform, we're done.
if (attr.isPlatformSpecific() && (!bestActive || attr != bestActive))
continue;

// Unconditional unavailable.
if (attr.isUnconditionallyUnavailable())
return attr;

switch (attr.getVersionAvailability(ctx)) {
case AvailableVersionComparison::Available:
case AvailableVersionComparison::PotentiallyUnavailable:
break;

case AvailableVersionComparison::Obsoleted:
case AvailableVersionComparison::Unavailable:
result.emplace(attr);
break;
}
}
return result;
}

std::optional<SemanticAvailableAttr> Decl::getUnavailableAttr() const {
if (auto attr = getDeclUnavailableAttr(this))
return attr;

// If D is an extension member, check if the extension is unavailable.
//
// Skip decls imported from Clang, they could be associated to the wrong
// extension and inherit undesired unavailability. The ClangImporter
// associates Objective-C protocol members to the first category where the
// protocol is directly or indirectly adopted, no matter its availability
// and the availability of other categories. rdar://problem/53956555
if (!getClangNode())
if (auto ext = dyn_cast<ExtensionDecl>(getDeclContext()))
return ext->getUnavailableAttr();
auto context = AvailabilityContext::forDeploymentTarget(getASTContext());
if (auto constraint = getAvailabilityConstraintsForDecl(this, context)
.getPrimaryConstraint()) {
if (constraint->isUnavailable())
return constraint->getAttr();
}

return std::nullopt;
}
Expand Down
Loading