Skip to content

Commit 2db13ee

Browse files
authored
Merge pull request #79566 from tshortli/semantic-availability-spec
2 parents 35a4175 + ae93451 commit 2db13ee

15 files changed

+223
-88
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/AvailabilityDomain.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,15 +280,23 @@ class CustomAvailabilityDomain : public ASTAllocated<CustomAvailabilityDomain> {
280280
/// Represents either a resolved availability domain or an identifier written
281281
/// in source that has not yet been resolved to a domain.
282282
class AvailabilityDomainOrIdentifier {
283+
friend struct llvm::PointerLikeTypeTraits<AvailabilityDomainOrIdentifier>;
284+
283285
using Storage = llvm::PointerUnion<AvailabilityDomain, Identifier>;
284286
Storage storage;
285287

288+
AvailabilityDomainOrIdentifier(Storage storage) : storage(storage) {}
289+
286290
public:
287291
AvailabilityDomainOrIdentifier(Identifier identifier)
288292
: storage(identifier) {};
289293
AvailabilityDomainOrIdentifier(AvailabilityDomain domain)
290294
: storage(domain) {};
291295

296+
static AvailabilityDomainOrIdentifier fromOpaque(void *opaque) {
297+
return AvailabilityDomainOrIdentifier(Storage::getFromOpaqueValue(opaque));
298+
}
299+
292300
bool isDomain() const { return storage.is<AvailabilityDomain>(); }
293301
bool isIdentifier() const { return storage.is<Identifier>(); }
294302

@@ -315,6 +323,7 @@ class AvailabilityDomainOrIdentifier {
315323

316324
namespace llvm {
317325
using swift::AvailabilityDomain;
326+
using swift::AvailabilityDomainOrIdentifier;
318327

319328
// An AvailabilityDomain is "pointer like".
320329
template <typename T>
@@ -352,6 +361,25 @@ struct DenseMapInfo<AvailabilityDomain> {
352361
}
353362
};
354363

364+
// An AvailabilityDomainOrIdentifier is "pointer like".
365+
template <typename T>
366+
struct PointerLikeTypeTraits;
367+
template <>
368+
struct PointerLikeTypeTraits<swift::AvailabilityDomainOrIdentifier> {
369+
public:
370+
static inline void *getAsVoidPointer(AvailabilityDomainOrIdentifier value) {
371+
return value.storage.getOpaqueValue();
372+
}
373+
static inline swift::AvailabilityDomainOrIdentifier
374+
getFromVoidPointer(void *P) {
375+
return AvailabilityDomainOrIdentifier::fromOpaque(P);
376+
}
377+
enum {
378+
NumLowBitsAvailable = PointerLikeTypeTraits<
379+
AvailabilityDomainOrIdentifier::Storage>::NumLowBitsAvailable
380+
};
381+
};
382+
355383
} // end namespace llvm
356384

357385
#endif

include/swift/AST/AvailabilityScope.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,8 @@ class AvailabilityScope : public ASTAllocated<AvailabilityScope> {
264264
/// condition that introduced the availability scope for a given platform
265265
/// version; if zero or multiple such responsible attributes or statements
266266
/// exist, returns an invalid SourceRange.
267-
SourceRange
268-
getAvailabilityConditionVersionSourceRange(
269-
PlatformKind Platform,
270-
const llvm::VersionTuple &Version) const;
267+
SourceRange getAvailabilityConditionVersionSourceRange(
268+
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const;
271269

272270
/// Returns the availability version range that was explicitly written in
273271
/// source, if applicable. Otherwise, returns null.

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: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -265,31 +265,31 @@ SourceLoc AvailabilityScope::getIntroductionLoc() const {
265265
llvm_unreachable("Unhandled Reason in switch.");
266266
}
267267

268-
static SourceRange
269-
getAvailabilityConditionVersionSourceRange(const PoundAvailableInfo *PAI,
270-
PlatformKind Platform,
271-
const llvm::VersionTuple &Version) {
268+
static SourceRange getAvailabilityConditionVersionSourceRange(
269+
const PoundAvailableInfo *PAI, const DeclContext *ReferenceDC,
270+
AvailabilityDomain Domain, const llvm::VersionTuple &Version) {
272271
SourceRange Range;
273-
for (auto *Spec : PAI->getQueries()) {
274-
if (Spec->getPlatform() == Platform && Spec->getVersion() == Version) {
272+
for (auto Spec : PAI->getSemanticAvailabilitySpecs(ReferenceDC)) {
273+
if (Spec.getDomain() == Domain && Spec.getVersion() == Version) {
275274
// More than one: return invalid range, no unique choice.
276275
if (Range.isValid())
277276
return SourceRange();
278277
else
279-
Range = Spec->getVersionSrcRange();
278+
Range = Spec.getParsedSpec()->getVersionSrcRange();
280279
}
281280
}
282281
return Range;
283282
}
284283

285284
static SourceRange getAvailabilityConditionVersionSourceRange(
286-
const MutableArrayRef<StmtConditionElement> &Conds, PlatformKind Platform,
285+
const MutableArrayRef<StmtConditionElement> &Conds,
286+
const DeclContext *ReferenceDC, AvailabilityDomain Domain,
287287
const llvm::VersionTuple &Version) {
288288
SourceRange Range;
289289
for (auto const &C : Conds) {
290290
if (C.getKind() == StmtConditionElement::CK_Availability) {
291291
SourceRange R = getAvailabilityConditionVersionSourceRange(
292-
C.getAvailability(), Platform, Version);
292+
C.getAvailability(), ReferenceDC, Domain, Version);
293293
// More than one: return invalid range.
294294
if (Range.isValid())
295295
return SourceRange();
@@ -301,13 +301,13 @@ static SourceRange getAvailabilityConditionVersionSourceRange(
301301
}
302302

303303
static SourceRange
304-
getAvailabilityConditionVersionSourceRange(const Decl *D, PlatformKind Platform,
304+
getAvailabilityConditionVersionSourceRange(const Decl *D,
305+
AvailabilityDomain Domain,
305306
const llvm::VersionTuple &Version) {
306307
SourceRange Range;
307308
for (auto Attr : D->getSemanticAvailableAttrs()) {
308309
if (Attr.getIntroduced().has_value() &&
309-
Attr.getIntroduced().value() == Version &&
310-
Attr.getPlatform() == Platform) {
310+
Attr.getIntroduced().value() == Version && Attr.getDomain() == Domain) {
311311

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

322322
SourceRange AvailabilityScope::getAvailabilityConditionVersionSourceRange(
323-
PlatformKind Platform, const llvm::VersionTuple &Version) const {
323+
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const {
324324
switch (getReason()) {
325325
case Reason::Decl:
326326
return ::getAvailabilityConditionVersionSourceRange(Node.getAsDecl(),
327-
Platform, Version);
327+
Domain, Version);
328328

329329
case Reason::IfStmtThenBranch:
330330
case Reason::IfStmtElseBranch:
331331
return ::getAvailabilityConditionVersionSourceRange(
332-
Node.getAsIfStmt()->getCond(), Platform, Version);
332+
Node.getAsIfStmt()->getCond(), Node.getDeclContext(), Domain, Version);
333333

334334
case Reason::ConditionFollowingAvailabilityQuery:
335335
return ::getAvailabilityConditionVersionSourceRange(
336-
Node.getAsPoundAvailableInfo(), Platform, Version);
336+
Node.getAsPoundAvailableInfo(), Node.getDeclContext(), Domain, Version);
337337

338338
case Reason::GuardStmtFallthrough:
339339
case Reason::GuardStmtElseBranch:
340340
return ::getAvailabilityConditionVersionSourceRange(
341-
Node.getAsGuardStmt()->getCond(), Platform, Version);
341+
Node.getAsGuardStmt()->getCond(), Node.getDeclContext(), Domain,
342+
Version);
342343

343344
case Reason::WhileStmtBody:
344345
return ::getAvailabilityConditionVersionSourceRange(
345-
Node.getAsWhileStmt()->getCond(), Platform, Version);
346+
Node.getAsWhileStmt()->getCond(), Node.getDeclContext(), Domain,
347+
Version);
346348

347349
case Reason::Root:
348350
case Reason::DeclImplicit:

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) {

0 commit comments

Comments
 (0)