Skip to content

Commit eb6506a

Browse files
committed
AST: Introduce SemanticAvailabilitySpec.
It wraps an type-checked `AvailabilitySpec`, which guarantees that the spec has a valid `AvailabilityDomain` associated with it. This will unblock moving AvailabilitySpec domain resolution from parsing to sema.
1 parent 5bccbe5 commit eb6506a

File tree

13 files changed

+173
-66
lines changed

13 files changed

+173
-66
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,9 @@ SWIFT_NAME("getter:BridgedAvailabilitySpec.platform(self:)")
732732
BridgedPlatformKind
733733
BridgedAvailabilitySpec_getPlatform(BridgedAvailabilitySpec spec);
734734

735-
SWIFT_NAME("getter:BridgedAvailabilitySpec.version(self:)")
735+
SWIFT_NAME("getter:BridgedAvailabilitySpec.rawVersion(self:)")
736736
BridgedVersionTuple
737-
BridgedAvailabilitySpec_getVersion(BridgedAvailabilitySpec spec);
737+
BridgedAvailabilitySpec_getRawVersion(BridgedAvailabilitySpec spec);
738738

739739
SWIFT_NAME("getter:BridgedAvailabilitySpec.versionRange(self:)")
740740
BridgedSourceRange

include/swift/AST/AvailabilitySpec.h

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
#include "swift/AST/AvailabilityDomain.h"
2222
#include "swift/AST/Identifier.h"
2323
#include "swift/AST/PlatformKind.h"
24+
#include "swift/Basic/STLExtras.h"
2425
#include "swift/Basic/SourceLoc.h"
2526
#include "llvm/ADT/DenseMap.h"
2627
#include "llvm/ADT/StringMap.h"
28+
#include "llvm/ADT/iterator_range.h"
2729
#include "llvm/Support/VersionTuple.h"
2830

2931
namespace swift {
@@ -45,6 +47,8 @@ enum class AvailabilitySpecKind {
4547
PackageDescriptionVersionConstraint,
4648
};
4749

50+
class SemanticAvailabilitySpec;
51+
4852
/// The root class for specifications of API availability in availability
4953
/// queries.
5054
class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
@@ -109,7 +113,14 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
109113

110114
AvailabilitySpecKind getKind() const { return Kind; }
111115

112-
bool isWildcard() { return getKind() == AvailabilitySpecKind::Wildcard; }
116+
bool isWildcard() const {
117+
return getKind() == AvailabilitySpecKind::Wildcard;
118+
}
119+
120+
/// Returns a type-checked representation of the spec, or `std::nullopt` if
121+
/// the spec is invalid.
122+
std::optional<SemanticAvailabilitySpec>
123+
getSemanticAvailabilitySpec(const DeclContext *declContext) const;
113124

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

125-
// The platform version to compare against.
126-
llvm::VersionTuple getVersion() const;
127-
128-
// The version to be used in codegen for version comparisons at run time.
129-
// This is required to support beta versions of macOS Big Sur that
130-
// report 10.16 at run time.
131-
llvm::VersionTuple getRuntimeVersion() const { return Version; }
136+
// The version tuple that was written in source.
137+
llvm::VersionTuple getRawVersion() const { return Version; }
132138

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

150+
/// The type-checked representation of `AvailabilitySpec` which guaranatees that
151+
/// the spec has a valid `AvailabilityDomain`.
152+
class SemanticAvailabilitySpec {
153+
const AvailabilitySpec *spec;
154+
155+
public:
156+
SemanticAvailabilitySpec(const AvailabilitySpec *spec) : spec(spec) {
157+
// The domain must be resolved in order to wrap it in a semantic spec.
158+
ASSERT(spec->isWildcard() || spec->getDomain());
159+
}
160+
161+
const AvailabilitySpec *getParsedSpec() const { return spec; }
162+
163+
AvailabilityDomain getDomain() const {
164+
if (isWildcard())
165+
return AvailabilityDomain::forUniversal();
166+
return spec->getDomain().value();
167+
}
168+
169+
bool isWildcard() const { return spec->isWildcard(); }
170+
171+
// The platform version to compare against.
172+
llvm::VersionTuple getVersion() const;
173+
174+
// The version to be used in codegen for version comparisons at run time.
175+
// This is required to support beta versions of macOS Big Sur that
176+
// report 10.16 at run time.
177+
llvm::VersionTuple getRuntimeVersion() const { return spec->getRawVersion(); }
178+
};
179+
180+
/// Wraps an array of availability specs and provides an iterator for their
181+
/// semantic representations.
182+
class SemanticAvailabilitySpecs {
183+
public:
184+
class Filter final {
185+
const DeclContext *declContext;
186+
187+
public:
188+
Filter(const DeclContext *declContext) : declContext(declContext) {}
189+
190+
std::optional<SemanticAvailabilitySpec>
191+
operator()(const AvailabilitySpec *spec) const;
192+
};
193+
194+
using Range = OptionalTransformRange<
195+
iterator_range<ArrayRef<AvailabilitySpec *>::const_iterator>, Filter>;
196+
197+
private:
198+
Range specRange;
199+
200+
public:
201+
SemanticAvailabilitySpecs(ArrayRef<AvailabilitySpec *> specs,
202+
const DeclContext *declContext)
203+
: specRange(llvm::make_range(specs.begin(), specs.end()),
204+
Filter(declContext)) {}
205+
206+
Range::iterator begin() const { return specRange.begin(); }
207+
Range::iterator end() const { return specRange.end(); }
208+
bool empty() const { return specRange.empty(); }
209+
};
210+
144211
/// Maps of macro name and version to availability specifications.
145212
/// Organized as two nested \c DenseMap keyed first on the macro name then
146213
/// the macro version. This structure allows to peek at macro names before

include/swift/AST/Stmt.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class FuncDecl;
4444
class AbstractFunctionDecl;
4545
class Pattern;
4646
class PatternBindingDecl;
47+
class SemanticAvailabilitySpecs;
4748
class VarDecl;
4849
class CaseStmt;
4950
class DoCatchStmt;
@@ -516,7 +517,11 @@ class alignas(8) PoundAvailableInfo final :
516517
ArrayRef<AvailabilitySpec *> getQueries() const {
517518
return llvm::ArrayRef(getTrailingObjects<AvailabilitySpec *>(), NumQueries);
518519
}
519-
520+
521+
/// Returns an iterator for the statement's type-checked availability specs.
522+
SemanticAvailabilitySpecs
523+
getSemanticAvailabilitySpecs(const DeclContext *declContext) const;
524+
520525
SourceLoc getLParenLoc() const { return LParenLoc; }
521526
SourceLoc getRParenLoc() const { return RParenLoc; }
522527

lib/AST/ASTDumper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,9 +1108,9 @@ namespace {
11081108
? "*"
11091109
: Spec->getDomain()->getNameForAttributePrinting();
11101110
printField(domainName, Label::always("domain"));
1111-
if (!Spec->getVersion().empty())
1111+
if (!Spec->getRawVersion().empty())
11121112
printFieldRaw(
1113-
[&](llvm::raw_ostream &OS) { OS << Spec->getVersion(); },
1113+
[&](llvm::raw_ostream &OS) { OS << Spec->getRawVersion(); },
11141114
Label::always("version"));
11151115
printFoot();
11161116
},

lib/AST/AvailabilityScope.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,13 @@ static SourceRange getAvailabilityConditionVersionSourceRange(
269269
const PoundAvailableInfo *PAI, const DeclContext *ReferenceDC,
270270
AvailabilityDomain Domain, const llvm::VersionTuple &Version) {
271271
SourceRange Range;
272-
for (auto *Spec : PAI->getQueries()) {
273-
if (Spec->getDomain() == Domain && Spec->getVersion() == Version) {
272+
for (auto Spec : PAI->getSemanticAvailabilitySpecs(ReferenceDC)) {
273+
if (Spec.getDomain() == Domain && Spec.getVersion() == Version) {
274274
// More than one: return invalid range, no unique choice.
275275
if (Range.isValid())
276276
return SourceRange();
277277
else
278-
Range = Spec->getVersionSrcRange();
278+
Range = Spec.getParsedSpec()->getVersionSrcRange();
279279
}
280280
}
281281
return Range;

lib/AST/AvailabilitySpec.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ AvailabilitySpec *AvailabilitySpec::createPlatformVersioned(
6565
SourceRange(platformLoc, versionRange.End), version, versionRange.Start);
6666
}
6767

68-
llvm::VersionTuple AvailabilitySpec::getVersion() const {
68+
llvm::VersionTuple SemanticAvailabilitySpec::getVersion() const {
6969
// For macOS Big Sur, we canonicalize 10.16 to 11.0 for compile-time
7070
// checking since clang canonicalizes availability markup. However, to
7171
// support Beta versions of macOS Big Sur where the OS
@@ -77,5 +77,22 @@ llvm::VersionTuple AvailabilitySpec::getVersion() const {
7777
//
7878
// we need to store the uncanonicalized version for codegen and canonicalize
7979
// it as necessary for compile-time checks.
80-
return canonicalizePlatformVersion(getPlatform(), Version);
80+
return canonicalizePlatformVersion(getDomain().getPlatformKind(),
81+
spec->getRawVersion());
82+
}
83+
84+
std::optional<SemanticAvailabilitySpec>
85+
AvailabilitySpec::getSemanticAvailabilitySpec(
86+
const DeclContext *declContext) const {
87+
if (isWildcard() || getDomain())
88+
return SemanticAvailabilitySpec(this);
89+
return std::nullopt;
90+
}
91+
92+
std::optional<SemanticAvailabilitySpec>
93+
SemanticAvailabilitySpecs::Filter::operator()(
94+
const AvailabilitySpec *spec) const {
95+
if (auto semanticSpec = spec->getSemanticAvailabilitySpec(declContext))
96+
return semanticSpec;
97+
return std::nullopt;
8198
}

lib/AST/Bridging/AvailabilityBridging.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ BridgedAvailabilitySpec_getPlatform(BridgedAvailabilitySpec spec) {
144144
}
145145

146146
BridgedVersionTuple
147-
BridgedAvailabilitySpec_getVersion(BridgedAvailabilitySpec spec) {
148-
return spec.unbridged()->getVersion();
147+
BridgedAvailabilitySpec_getRawVersion(BridgedAvailabilitySpec spec) {
148+
return spec.unbridged()->getRawVersion();
149149
}
150150

151151
BridgedSourceRange

lib/AST/Stmt.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,11 @@ PoundAvailableInfo::create(ASTContext &ctx, SourceLoc PoundLoc,
619619
RParenLoc, isUnavailability);
620620
}
621621

622+
SemanticAvailabilitySpecs PoundAvailableInfo::getSemanticAvailabilitySpecs(
623+
const DeclContext *declContext) const {
624+
return SemanticAvailabilitySpecs(getQueries(), declContext);
625+
}
626+
622627
SourceLoc PoundAvailableInfo::getEndLoc() const {
623628
if (RParenLoc.isInvalid()) {
624629
if (NumQueries == 0) {

lib/ASTGen/Sources/ASTGen/Availability.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ extension ASTGenVisitor {
8585
kind: .default,
8686
message: BridgedStringRef(),
8787
renamed: BridgedStringRef(),
88-
introduced: spec.version,
88+
introduced: spec.rawVersion,
8989
introducedRange: spec.versionRange,
9090
deprecated: BridgedVersionTuple(),
9191
deprecatedRange: BridgedSourceRange(),
@@ -392,7 +392,7 @@ extension ASTGenVisitor {
392392
guard platform != .none else {
393393
continue
394394
}
395-
result.append((platform: platform, version: spec.version))
395+
result.append((platform: platform, version: spec.rawVersion))
396396
}
397397
}
398398
continue

lib/ConstExtract/ConstExtract.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,11 +1052,12 @@ getConditionalMemberFromIfStmt(const IfStmt *ifStmt,
10521052
}
10531053
for (auto elt : ifStmt->getCond()) {
10541054
if (elt.getKind() == StmtConditionElement::CK_Availability) {
1055-
for (auto *Q : elt.getAvailability()->getQueries()) {
1056-
if (Q->getPlatform() != PlatformKind::none) {
1057-
auto spec = BuilderValue::ConditionalMember::AvailabilitySpec(
1058-
*Q->getDomain(), Q->getVersion());
1059-
AvailabilitySpecs.push_back(spec);
1055+
for (auto spec :
1056+
elt.getAvailability()->getSemanticAvailabilitySpecs(declContext)) {
1057+
if (spec.getDomain().isPlatform()) {
1058+
AvailabilitySpecs.push_back(
1059+
BuilderValue::ConditionalMember::AvailabilitySpec(
1060+
spec.getDomain(), spec.getVersion()));
10601061
}
10611062
}
10621063
memberKind = BuilderValue::LimitedAvailability;

0 commit comments

Comments
 (0)