Skip to content

Commit 1e7cc98

Browse files
authored
Merge pull request #77083 from tshortli/introduce-availability-context
2 parents 7a31f39 + 47e4185 commit 1e7cc98

File tree

6 files changed

+261
-83
lines changed

6 files changed

+261
-83
lines changed

include/swift/AST/Availability.h

Lines changed: 76 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,78 @@ 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{platformAvailability};
386+
return get(platformInfo, ctx);
387+
}
388+
389+
/// Returns the range of platform versions which may execute code in the
390+
/// availability context, starting at its introduction version.
391+
AvailabilityRange getPlatformRange() const {
392+
return PlatformAvailability.Range;
393+
}
394+
395+
/// Returns the unique context that is the result of constraining the current
396+
/// context's platform availability range with `platformRange`.
397+
const AvailabilityContext *
398+
constrainWithPlatformRange(const AvailabilityRange &platformRange,
399+
ASTContext &ctx) const;
400+
401+
/// Returns the unique context that is the result of constraining the current
402+
/// context both with the availability attributes of `decl` and with
403+
/// `platformRange`.
404+
const AvailabilityContext *constrainWithDeclAndPlatformRange(
405+
Decl *decl, const AvailabilityRange &platformRange) const;
406+
407+
void Profile(llvm::FoldingSetNodeID &ID) const;
408+
};
409+
334410
class AvailabilityInference {
335411
public:
336412
/// Returns the decl that should be considered the parent decl of the given

include/swift/AST/TypeRefinementContext.h

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
171171

172172
/// A canonical availability info for this context, computed top-down from the
173173
/// root context.
174-
AvailabilityRange AvailabilityInfo;
174+
const AvailabilityContext *AvailabilityInfo;
175175

176176
std::vector<TypeRefinementContext *> Children;
177177

@@ -184,33 +184,33 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
184184

185185
TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
186186
TypeRefinementContext *Parent, SourceRange SrcRange,
187-
const AvailabilityRange &Info);
187+
const AvailabilityContext *Info);
188188

189189
public:
190190
/// Create the root refinement context for the given SourceFile.
191191
static TypeRefinementContext *
192-
createForSourceFile(SourceFile *SF, const AvailabilityRange &Info);
192+
createForSourceFile(SourceFile *SF, const AvailabilityContext *Info);
193193

194194
/// Create a refinement context for the given declaration.
195195
static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D,
196196
TypeRefinementContext *Parent,
197-
const AvailabilityRange &Info,
197+
const AvailabilityContext *Info,
198198
SourceRange SrcRange);
199199

200200
/// Create a refinement context for the given declaration.
201201
static TypeRefinementContext *
202202
createForDeclImplicit(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent,
203-
const AvailabilityRange &Info, SourceRange SrcRange);
203+
const AvailabilityContext *Info, SourceRange SrcRange);
204204

205205
/// Create a refinement context for the Then branch of the given IfStmt.
206206
static TypeRefinementContext *
207207
createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent,
208-
const AvailabilityRange &Info);
208+
const AvailabilityContext *Info);
209209

210210
/// Create a refinement context for the Else branch of the given IfStmt.
211211
static TypeRefinementContext *
212212
createForIfStmtElse(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent,
213-
const AvailabilityRange &Info);
213+
const AvailabilityContext *Info);
214214

215215
/// Create a refinement context for the true-branch control flow to
216216
/// further StmtConditionElements following a #available() query in
@@ -219,24 +219,24 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
219219
createForConditionFollowingQuery(ASTContext &Ctx, PoundAvailableInfo *PAI,
220220
const StmtConditionElement &LastElement,
221221
TypeRefinementContext *Parent,
222-
const AvailabilityRange &Info);
222+
const AvailabilityContext *Info);
223223

224224
/// Create a refinement context for the fallthrough of a GuardStmt.
225225
static TypeRefinementContext *createForGuardStmtFallthrough(
226226
ASTContext &Ctx, GuardStmt *RS, BraceStmt *ContainingBraceStmt,
227-
TypeRefinementContext *Parent, const AvailabilityRange &Info);
227+
TypeRefinementContext *Parent, const AvailabilityContext *Info);
228228

229229
/// Create a refinement context for the else branch of a GuardStmt.
230230
static TypeRefinementContext *
231231
createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS,
232232
TypeRefinementContext *Parent,
233-
const AvailabilityRange &Info);
233+
const AvailabilityContext *Info);
234234

235235
/// Create a refinement context for the body of a WhileStmt.
236236
static TypeRefinementContext *
237237
createForWhileStmtBody(ASTContext &Ctx, WhileStmt *WS,
238238
TypeRefinementContext *Parent,
239-
const AvailabilityRange &Info);
239+
const AvailabilityContext *Info);
240240

241241
Decl *getDeclOrNull() const {
242242
auto IntroReason = getReason();
@@ -276,12 +276,17 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
276276
/// Returns the source range on which this context refines types.
277277
SourceRange getSourceRange() const { return SrcRange; }
278278

279-
/// Returns the information on what can be assumed present at run time when
280-
/// running code contained in this context.
281-
const AvailabilityRange &getAvailabilityInfo() const {
279+
/// Returns the availability context of code contained in this context.
280+
const AvailabilityContext *getAvailabilityContext() const {
282281
return AvailabilityInfo;
283282
}
284283

284+
/// Returns the platform version range that can be assumed present at run
285+
/// time when running code contained in this context.
286+
const AvailabilityRange getPlatformAvailabilityRange() const {
287+
return AvailabilityInfo->getPlatformRange();
288+
}
289+
285290
/// Adds a child refinement context.
286291
void addChild(TypeRefinementContext *Child, ASTContext &Ctx);
287292

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