Skip to content

Commit 439c595

Browse files
committed
AST: Introduce AvailabilityContext.
This class is designed to be a compact representation of the active availability constraints in a specific scope. For now, it only models platform introduction availability but it will soon be updated to cover additional availability constraints, like platform unavailability. In anticipation of needing to reference `AvailabilityContext`s from `TypeRefinementContext`s and increasing memory requirements for these contexts, a cache of uniqued instances of `AvailabilityContext` are stored in a `llvm::FoldingSet` on `ASTContext`.
1 parent 763666e commit 439c595

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

include/swift/AST/Availability.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#ifndef SWIFT_AST_AVAILABILITY_H
1818
#define SWIFT_AST_AVAILABILITY_H
1919

20+
#include "swift/AST/PlatformKind.h"
2021
#include "swift/AST/Type.h"
2122
#include "swift/Basic/LLVM.h"
23+
#include "llvm/ADT/FoldingSet.h"
2224
#include "llvm/Support/VersionTuple.h"
2325
#include <optional>
2426

@@ -174,6 +176,8 @@ class VersionRange {
174176
return VersionRange(EndPoint);
175177
}
176178

179+
void Profile(llvm::FoldingSetNodeID &ID) const;
180+
177181
private:
178182
VersionRange(const llvm::VersionTuple &LowerEndpoint) {
179183
setLowerEndpoint(LowerEndpoint);
@@ -331,6 +335,80 @@ class AvailabilityRange {
331335
}
332336
};
333337

338+
/// An `AvailabilityContext` summarizes the availability constraints for a
339+
/// specific scope, such as within a declaration or at a particular source
340+
/// location in a function body. This context is sufficient to determine whether
341+
/// a declaration is available or not in that scope.
342+
class AvailabilityContext : public llvm::FoldingSetNode {
343+
/// Summarizes platform specific availability constraints.
344+
struct PlatformInfo {
345+
/// The introduction version.
346+
AvailabilityRange Range;
347+
348+
/// Sets `Range` to `other` if `other` is more restrictive. Returns true if
349+
/// any property changed as a result of adding this constraint.
350+
bool constrainRange(const AvailabilityRange &other) {
351+
if (!other.isContainedIn(Range))
352+
return false;
353+
354+
Range = other;
355+
return true;
356+
}
357+
358+
/// Sets `Range` to the platform introduction range of `decl` if that range
359+
/// is more restrictive. Returns true if
360+
/// any property changed as a result of adding this constraint.
361+
bool constrainRange(const Decl *decl);
362+
363+
void Profile(llvm::FoldingSetNodeID &ID) const {
364+
Range.getRawVersionRange().Profile(ID);
365+
}
366+
};
367+
PlatformInfo PlatformAvailability;
368+
369+
AvailabilityContext(const PlatformInfo &platformInfo)
370+
: PlatformAvailability(platformInfo){};
371+
372+
static const AvailabilityContext *get(const PlatformInfo &platformInfo,
373+
ASTContext &ctx);
374+
375+
public:
376+
/// Retrieves the default `AvailabilityContext`, which is maximally available.
377+
/// The platform availability range will be set to the deployment target (or
378+
/// minimum inlining target when applicable).
379+
static const AvailabilityContext *getDefault(ASTContext &ctx);
380+
381+
/// Retrieves a uniqued `AvailabilityContext` with the given platform
382+
/// availability parameters.
383+
static const AvailabilityContext *
384+
get(const AvailabilityRange &platformAvailability, ASTContext &ctx) {
385+
PlatformInfo platformInfo{
386+
.Range = platformAvailability,
387+
};
388+
return get(platformInfo, ctx);
389+
}
390+
391+
/// Returns the range of platform versions which may execute code in the
392+
/// availability context, starting at its introduction version.
393+
AvailabilityRange getPlatformRange() const {
394+
return PlatformAvailability.Range;
395+
}
396+
397+
/// Returns the unique context that is the result of constraining the current
398+
/// context's platform availability range with `platformRange`.
399+
const AvailabilityContext *
400+
constrainWithPlatformRange(const AvailabilityRange &platformRange,
401+
ASTContext &ctx) const;
402+
403+
/// Returns the unique context that is the result of constraining the current
404+
/// context both with the availability attributes of `decl` and with
405+
/// `platformRange`.
406+
const AvailabilityContext *constrainWithDeclAndPlatformRange(
407+
Decl *decl, const AvailabilityRange &platformRange) const;
408+
409+
void Profile(llvm::FoldingSetNodeID &ID) const;
410+
};
411+
334412
class AvailabilityInference {
335413
public:
336414
/// Returns the decl that should be considered the parent decl of the given

lib/AST/ASTContext.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,9 @@ struct ASTContext::Implementation {
593593
/// The set of substitution maps (uniqued by their storage).
594594
llvm::FoldingSet<SubstitutionMap::Storage> SubstitutionMaps;
595595

596+
/// The set of unique AvailabilityContexts.
597+
llvm::FoldingSet<AvailabilityContext> AvailabilityContexts;
598+
596599
~Arena() {
597600
for (auto &conformance : SpecializedConformances)
598601
conformance.~SpecializedProtocolConformance();
@@ -3301,6 +3304,7 @@ void ASTContext::Implementation::Arena::dump(llvm::raw_ostream &os) const {
33013304
SIZE_AND_BYTES(BuiltinConformances);
33023305
SIZE(PackConformances);
33033306
SIZE(SubstitutionMaps);
3307+
SIZE(AvailabilityContexts);
33043308

33053309
#undef SIZE
33063310
#undef SIZE_AND_BYTES
@@ -5577,6 +5581,26 @@ SubstitutionMap::Storage *SubstitutionMap::Storage::get(
55775581
return result;
55785582
}
55795583

5584+
const AvailabilityContext *
5585+
AvailabilityContext::get(const PlatformInfo &platformInfo, ASTContext &ctx) {
5586+
llvm::FoldingSetNodeID id;
5587+
platformInfo.Profile(id);
5588+
5589+
auto &foldingSet =
5590+
ctx.getImpl().getArena(AllocationArena::Permanent).AvailabilityContexts;
5591+
void *insertPos;
5592+
auto *existing = foldingSet.FindNodeOrInsertPos(id, insertPos);
5593+
if (existing)
5594+
return existing;
5595+
5596+
void *mem =
5597+
ctx.Allocate(sizeof(AvailabilityContext), alignof(AvailabilityContext));
5598+
auto *newNode = ::new (mem) AvailabilityContext(platformInfo);
5599+
foldingSet.InsertNode(newNode, insertPos);
5600+
5601+
return newNode;
5602+
}
5603+
55805604
void GenericSignatureImpl::Profile(llvm::FoldingSetNodeID &ID,
55815605
ArrayRef<GenericTypeParamType *> genericParams,
55825606
ArrayRef<Requirement> requirements) {

lib/AST/Availability.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@
2929

3030
using namespace swift;
3131

32+
void VersionRange::Profile(llvm::FoldingSetNodeID &id) const {
33+
id.AddBoolean(hasLowerEndpoint());
34+
if (!hasLowerEndpoint()) {
35+
id.AddBoolean(isAll());
36+
return;
37+
}
38+
39+
auto profileVersionComponent = [&id](std::optional<unsigned> component) {
40+
id.AddBoolean(component.has_value());
41+
if (component)
42+
id.AddInteger(*component);
43+
};
44+
45+
auto lowerEndpoint = getLowerEndpoint();
46+
id.AddInteger(lowerEndpoint.getMajor());
47+
profileVersionComponent(lowerEndpoint.getMinor());
48+
profileVersionComponent(lowerEndpoint.getSubminor());
49+
profileVersionComponent(lowerEndpoint.getBuild());
50+
}
51+
3252
AvailabilityRange
3353
AvailabilityRange::forDeploymentTarget(const ASTContext &Ctx) {
3454
return AvailabilityRange(
@@ -44,6 +64,47 @@ AvailabilityRange AvailabilityRange::forRuntimeTarget(const ASTContext &Ctx) {
4464
return AvailabilityRange(VersionRange::allGTE(Ctx.LangOpts.RuntimeVersion));
4565
}
4666

67+
bool AvailabilityContext::PlatformInfo::constrainRange(const Decl *decl) {
68+
if (auto range = AvailabilityInference::annotatedAvailableRange(decl))
69+
return constrainRange(*range);
70+
71+
return false;
72+
}
73+
74+
const AvailabilityContext *AvailabilityContext::getDefault(ASTContext &ctx) {
75+
PlatformInfo platformInfo{.Range = AvailabilityRange::forInliningTarget(ctx)};
76+
return AvailabilityContext::get(platformInfo, ctx);
77+
}
78+
79+
/// Returns the unique context that is the result of constraining the current
80+
/// context's platform availability range with `platformRange`.
81+
const AvailabilityContext *AvailabilityContext::constrainWithPlatformRange(
82+
const AvailabilityRange &platformRange, ASTContext &ctx) const {
83+
PlatformInfo platformAvailability{PlatformAvailability};
84+
if (platformAvailability.constrainRange(platformRange))
85+
return get(platformAvailability, ctx);
86+
87+
return this;
88+
}
89+
90+
const AvailabilityContext *
91+
AvailabilityContext::constrainWithDeclAndPlatformRange(
92+
Decl *decl, const AvailabilityRange &platformRange) const {
93+
PlatformInfo platformAvailability{PlatformAvailability};
94+
bool isConstrained = false;
95+
isConstrained |= platformAvailability.constrainRange(decl);
96+
isConstrained |= platformAvailability.constrainRange(platformRange);
97+
98+
if (!isConstrained)
99+
return this;
100+
101+
return get(platformAvailability, decl->getASTContext());
102+
}
103+
104+
void AvailabilityContext::Profile(llvm::FoldingSetNodeID &id) const {
105+
PlatformAvailability.Profile(id);
106+
}
107+
47108
namespace {
48109

49110
/// The inferred availability required to access a group of declarations

0 commit comments

Comments
 (0)