mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
AST: Optimize the layout of AvailabilityRange.
`AvailabilityRange` is now being used as a currency type in more of the compiler, and some of those uses are in permanent `ASTContext` allocations. The class wraps the `VersionRange` utility, which is itself a wrapper around `llvm::VersionTuple` with some additional storage for representing sentinel values. Even though the two sentinel values can be be represented with just a single bit of additional storage on top of the 16 bytes required to represent `VersionTuple`, because of alignment requirements the sentinel values end up bloating the layout of `VersionRange` by many bytes. To make `AvailabilityRange` and `VersionRange` more efficient to store, we can instead reserve two unlikely `llvm::VersionTuple` bit patterns as the sentinel values instead. The values chosen are the same ones LLVM uses to represent version tuple tombstones and empty keys in a `DenseMap`.
This commit is contained in:
@@ -514,7 +514,7 @@ enum ENUM_EXTENSIBILITY_ATTR(open) BridgedDiagID : uint32_t {
|
||||
};
|
||||
|
||||
class BridgedDiagnosticArgument {
|
||||
int64_t storage[4];
|
||||
int64_t storage[3];
|
||||
|
||||
public:
|
||||
BRIDGED_INLINE BridgedDiagnosticArgument(const swift::DiagnosticArgument &arg);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef SWIFT_AST_AVAILABILITY_RANGE_H
|
||||
#define SWIFT_AST_AVAILABILITY_RANGE_H
|
||||
|
||||
#include "swift/Basic/Assertions.h"
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/Support/VersionTuple.h"
|
||||
@@ -36,36 +37,39 @@ class VersionRange {
|
||||
// All: all versions
|
||||
// x.y.x: all versions greater than or equal to x.y.z
|
||||
|
||||
enum class ExtremalRange { Empty, All };
|
||||
/// The sentinel version tuple representing a range containing all versions.
|
||||
constexpr static llvm::VersionTuple getAllTuple() {
|
||||
return llvm::VersionTuple(0x7FFFFFFE);
|
||||
}
|
||||
|
||||
/// The sentinel version tuple representing an empty range.
|
||||
constexpr static llvm::VersionTuple getEmptyTuple() {
|
||||
return llvm::VersionTuple(0x7FFFFFFF);
|
||||
}
|
||||
|
||||
// A version range is either an extremal value (Empty, All) or
|
||||
// a single version tuple value representing the lower end point x.y.z of a
|
||||
// range [x.y.z, +Inf).
|
||||
union {
|
||||
llvm::VersionTuple LowerEndpoint;
|
||||
ExtremalRange ExtremalValue;
|
||||
};
|
||||
|
||||
unsigned HasLowerEndpoint : 1;
|
||||
llvm::VersionTuple LowerEndpoint;
|
||||
|
||||
public:
|
||||
/// Returns true if the range of versions is empty, or false otherwise.
|
||||
bool isEmpty() const {
|
||||
return !HasLowerEndpoint && ExtremalValue == ExtremalRange::Empty;
|
||||
return !hasLowerEndpoint() && LowerEndpoint == getEmptyTuple();
|
||||
}
|
||||
|
||||
/// Returns true if the range includes all versions, or false otherwise.
|
||||
bool isAll() const {
|
||||
return !HasLowerEndpoint && ExtremalValue == ExtremalRange::All;
|
||||
return !hasLowerEndpoint() && LowerEndpoint == getAllTuple();
|
||||
}
|
||||
|
||||
/// Returns true if the range has a lower end point; that is, if it is of
|
||||
/// the form [X, +Inf).
|
||||
bool hasLowerEndpoint() const { return HasLowerEndpoint; }
|
||||
bool hasLowerEndpoint() const { return isValidVersion(LowerEndpoint); }
|
||||
|
||||
/// Returns the range's lower endpoint.
|
||||
const llvm::VersionTuple &getLowerEndpoint() const {
|
||||
assert(HasLowerEndpoint);
|
||||
assert(hasLowerEndpoint());
|
||||
return LowerEndpoint;
|
||||
}
|
||||
|
||||
@@ -124,7 +128,7 @@ public:
|
||||
const llvm::VersionTuple maxVersion =
|
||||
std::max(this->getLowerEndpoint(), Other.getLowerEndpoint());
|
||||
|
||||
setLowerEndpoint(maxVersion);
|
||||
LowerEndpoint = maxVersion;
|
||||
}
|
||||
|
||||
/// Mutates this range to be the union of itself and Other. This is the
|
||||
@@ -145,7 +149,7 @@ public:
|
||||
const llvm::VersionTuple minVersion =
|
||||
std::min(this->getLowerEndpoint(), Other.getLowerEndpoint());
|
||||
|
||||
setLowerEndpoint(minVersion);
|
||||
LowerEndpoint = minVersion;
|
||||
}
|
||||
|
||||
/// Mutates this range to be a best effort over-approximation of the
|
||||
@@ -160,37 +164,29 @@ public:
|
||||
}
|
||||
|
||||
/// Returns a version range representing all versions.
|
||||
static VersionRange all() { return VersionRange(ExtremalRange::All); }
|
||||
static VersionRange all() { return VersionRange(getAllTuple()); }
|
||||
|
||||
/// Returns a version range representing no versions.
|
||||
static VersionRange empty() { return VersionRange(ExtremalRange::Empty); }
|
||||
static VersionRange empty() { return VersionRange(getEmptyTuple()); }
|
||||
|
||||
/// Returns false if the given version tuple cannot be used as a lower
|
||||
/// endpoint for `VersionRange`.
|
||||
static bool isValidVersion(const llvm::VersionTuple &EndPoint) {
|
||||
return EndPoint != getAllTuple() && EndPoint != getEmptyTuple();
|
||||
}
|
||||
|
||||
/// Returns a version range representing all versions greater than or equal
|
||||
/// to the passed-in version.
|
||||
static VersionRange allGTE(const llvm::VersionTuple &EndPoint) {
|
||||
ASSERT(isValidVersion(EndPoint));
|
||||
return VersionRange(EndPoint);
|
||||
}
|
||||
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const;
|
||||
|
||||
private:
|
||||
VersionRange(const llvm::VersionTuple &LowerEndpoint) {
|
||||
setLowerEndpoint(LowerEndpoint);
|
||||
}
|
||||
|
||||
VersionRange(ExtremalRange ExtremalValue) {
|
||||
setExtremalRange(ExtremalValue);
|
||||
}
|
||||
|
||||
void setExtremalRange(ExtremalRange Version) {
|
||||
HasLowerEndpoint = 0;
|
||||
ExtremalValue = Version;
|
||||
}
|
||||
|
||||
void setLowerEndpoint(const llvm::VersionTuple &Version) {
|
||||
HasLowerEndpoint = 1;
|
||||
LowerEndpoint = Version;
|
||||
}
|
||||
VersionRange(const llvm::VersionTuple &LowerEndpoint)
|
||||
: LowerEndpoint(LowerEndpoint) {}
|
||||
};
|
||||
|
||||
/// Represents a version range in which something is available.
|
||||
@@ -327,7 +323,7 @@ public:
|
||||
/// Returns a representation of the raw version range as a string for
|
||||
/// debugging purposes.
|
||||
std::string getVersionString() const {
|
||||
assert(Range.hasLowerEndpoint());
|
||||
ASSERT(Range.hasLowerEndpoint());
|
||||
return Range.getLowerEndpoint().getAsString();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6754,6 +6754,8 @@ WARNING(availability_suggest_platform_name,
|
||||
PointsToFirstBadToken, "unrecognized platform name %0;"
|
||||
" did you mean '%1'?",
|
||||
(Identifier, StringRef))
|
||||
WARNING(availability_unsupported_version_number, none,
|
||||
"'%0' is not a supported version number", (llvm::VersionTuple))
|
||||
|
||||
WARNING(attr_availability_expected_deprecated_version, none,
|
||||
"expected version number with 'deprecated' in '%0' attribute for %1",
|
||||
|
||||
@@ -769,7 +769,25 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
|
||||
if (!domain)
|
||||
return std::nullopt;
|
||||
|
||||
auto semanticAttr = SemanticAvailableAttr(attr);
|
||||
auto checkVersion = [&](std::optional<llvm::VersionTuple> version,
|
||||
SourceRange sourceRange) {
|
||||
if (version && !VersionRange::isValidVersion(*version)) {
|
||||
diags
|
||||
.diagnose(attrLoc, diag::availability_unsupported_version_number,
|
||||
*version)
|
||||
.highlight(sourceRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (checkVersion(attr->getRawIntroduced(), attr->IntroducedRange))
|
||||
return std::nullopt;
|
||||
if (checkVersion(attr->getRawDeprecated(), attr->DeprecatedRange))
|
||||
return std::nullopt;
|
||||
if (checkVersion(attr->getRawObsoleted(), attr->ObsoletedRange))
|
||||
return std::nullopt;
|
||||
|
||||
bool hasIntroduced = attr->getRawIntroduced().has_value();
|
||||
bool hasDeprecated = attr->getRawDeprecated().has_value();
|
||||
@@ -779,11 +797,11 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
|
||||
if (!domain->isVersioned() && hasVersionSpec) {
|
||||
SourceRange versionSourceRange;
|
||||
if (hasIntroduced)
|
||||
versionSourceRange = semanticAttr.getIntroducedSourceRange();
|
||||
versionSourceRange = attr->IntroducedRange;
|
||||
else if (hasDeprecated)
|
||||
versionSourceRange = semanticAttr.getDeprecatedSourceRange();
|
||||
versionSourceRange = attr->DeprecatedRange;
|
||||
else if (hasObsoleted)
|
||||
versionSourceRange = semanticAttr.getObsoletedSourceRange();
|
||||
versionSourceRange = attr->ObsoletedRange;
|
||||
|
||||
diags.diagnose(attrLoc, diag::availability_unexpected_version, *domain)
|
||||
.limitBehaviorIf(domain->isUniversal(), DiagnosticBehavior::Warning)
|
||||
@@ -827,7 +845,7 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
|
||||
}
|
||||
}
|
||||
|
||||
return semanticAttr;
|
||||
return SemanticAvailableAttr(attr);
|
||||
}
|
||||
|
||||
std::optional<llvm::VersionTuple> SemanticAvailableAttr::getIntroduced() const {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "swift/AST/AvailabilitySpec.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/AvailabilityDomain.h"
|
||||
#include "swift/AST/DiagnosticsParse.h"
|
||||
#include "swift/AST/DiagnosticsSema.h"
|
||||
#include "swift/AST/TypeCheckRequests.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
@@ -116,21 +116,35 @@ std::optional<SemanticAvailabilitySpec>
|
||||
SemanticAvailabilitySpecRequest::evaluate(
|
||||
Evaluator &evaluator, const AvailabilitySpec *spec,
|
||||
const DeclContext *declContext) const {
|
||||
if (spec->isInvalid())
|
||||
return std::nullopt;
|
||||
|
||||
auto &diags = declContext->getASTContext().Diags;
|
||||
AvailabilitySpec *mutableSpec = const_cast<AvailabilitySpec *>(spec);
|
||||
if (mutableSpec->resolveInDeclContext(declContext).has_value())
|
||||
return SemanticAvailabilitySpec(spec);
|
||||
return std::nullopt;
|
||||
if (!mutableSpec->resolveInDeclContext(declContext).has_value())
|
||||
return std::nullopt;
|
||||
|
||||
auto version = spec->getRawVersion();
|
||||
if (!VersionRange::isValidVersion(version)) {
|
||||
diags
|
||||
.diagnose(spec->getStartLoc(),
|
||||
diag::availability_unsupported_version_number, version)
|
||||
.highlight(spec->getVersionSrcRange());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return SemanticAvailabilitySpec(spec);
|
||||
}
|
||||
|
||||
std::optional<std::optional<SemanticAvailabilitySpec>>
|
||||
SemanticAvailabilitySpecRequest::getCachedResult() const {
|
||||
auto *spec = std::get<0>(getStorage());
|
||||
if (spec->isInvalid())
|
||||
return std::optional<SemanticAvailabilitySpec>();
|
||||
|
||||
auto domainOrIdentifier = spec->getDomainOrIdentifier();
|
||||
if (!domainOrIdentifier.isResolved())
|
||||
return {};
|
||||
|
||||
if (spec->isInvalid())
|
||||
return {std::nullopt};
|
||||
return std::nullopt;
|
||||
|
||||
return SemanticAvailabilitySpec(spec);
|
||||
}
|
||||
|
||||
@@ -5158,6 +5158,17 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasVersion) {
|
||||
auto rawVersion = parsedSpec->getRawVersion();
|
||||
if (!VersionRange::isValidVersion(rawVersion)) {
|
||||
diags
|
||||
.diagnose(loc, diag::availability_unsupported_version_number,
|
||||
rawVersion)
|
||||
.highlight(parsedSpec->getVersionSrcRange());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (domain.isVersioned()) {
|
||||
if (!hasVersion) {
|
||||
diags.diagnose(loc, diag::avail_query_expected_version_number);
|
||||
|
||||
44
test/Sema/availability_versions_unsupported.swift
Normal file
44
test/Sema/availability_versions_unsupported.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
|
||||
@available(macOS, introduced: 2147483646) // expected-warning {{'2147483646' is not a supported version number}}
|
||||
func funcIntroducedInMacOS2147483646() { }
|
||||
|
||||
@available(macOS 2147483646, *) // expected-warning {{'2147483646' is not a supported version number}}
|
||||
func funcIntroducedInMacOS2147483646Short() { }
|
||||
|
||||
@available(macOS, deprecated: 2147483646) // expected-warning {{'2147483646' is not a supported version number}}
|
||||
func funcDeprecatedInMacOS2147483646() { }
|
||||
|
||||
@available(macOS, obsoleted: 2147483646) // expected-warning {{'2147483646' is not a supported version number}}
|
||||
func funcObsoletedInMacOS2147483646() { }
|
||||
|
||||
@available(macOS, introduced: 2147483647) // expected-warning {{'2147483647' is not a supported version number}}
|
||||
func funcIntroducedInMacOS2147483647() { }
|
||||
|
||||
@available(macOS 2147483647, *) // expected-warning {{'2147483647' is not a supported version number}}
|
||||
func funcIntroducedInMacOS2147483647Short() { }
|
||||
|
||||
@available(macOS, deprecated: 2147483647) // expected-warning {{'2147483647' is not a supported version number}}
|
||||
func funcDeprecatedInMacOS2147483647() { }
|
||||
|
||||
@available(macOS, obsoleted: 2147483647) // expected-warning {{'2147483647' is not a supported version number}}
|
||||
func funcObsoletedInMacOS2147483647() { }
|
||||
|
||||
@available(swift, introduced: 2147483646) // expected-warning {{'2147483646' is not a supported version number}}
|
||||
func funcIntroducedInSwift2147483646() { }
|
||||
|
||||
func useExtremeVersions() {
|
||||
if #available(macOS 2147483646, *) { // expected-warning {{'2147483646' is not a supported version number}}
|
||||
funcIntroducedInMacOS2147483646()
|
||||
funcIntroducedInMacOS2147483646Short()
|
||||
funcDeprecatedInMacOS2147483646()
|
||||
funcObsoletedInMacOS2147483646()
|
||||
}
|
||||
if #available(macOS 2147483647, *) { // expected-warning {{'2147483647' is not a supported version number}}
|
||||
funcIntroducedInMacOS2147483647()
|
||||
funcIntroducedInMacOS2147483647Short()
|
||||
funcDeprecatedInMacOS2147483647()
|
||||
funcObsoletedInMacOS2147483647()
|
||||
}
|
||||
funcIntroducedInSwift2147483646()
|
||||
}
|
||||
@@ -155,6 +155,12 @@ let _: Int
|
||||
@available(OSX, introduced: 0.0.0) // expected-warning{{expected version number in 'available' attribute; this is an error in the Swift 6 language mode}}
|
||||
let _: Int
|
||||
|
||||
@available(OSX, introduced: 2147483646)
|
||||
let _: Int
|
||||
|
||||
@available(OSX, introduced: 2147483647)
|
||||
let _: Int
|
||||
|
||||
@available(*, renamed: "bad name") // expected-error{{'renamed' argument of 'available' attribute must be an operator, identifier, or full function name, optionally prefixed by a type name}}
|
||||
let _: Int
|
||||
|
||||
|
||||
@@ -125,3 +125,9 @@ TEST_F(VersionRangeLattice, JoinWithClosedEndedPositiveInfinity) {
|
||||
EXPECT_TRUE(unionEquals(GreaterThanEqual10_10, GreaterThanEqual10_9,
|
||||
GreaterThanEqual10_9));
|
||||
}
|
||||
|
||||
TEST_F(VersionRangeLattice, ValidVersionTuples) {
|
||||
EXPECT_TRUE(VersionRange::isValidVersion(llvm::VersionTuple()));
|
||||
EXPECT_FALSE(VersionRange::isValidVersion(llvm::VersionTuple(0x7FFFFFFE)));
|
||||
EXPECT_FALSE(VersionRange::isValidVersion(llvm::VersionTuple(0x7FFFFFFF)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user