Skip to content

Commit 2dd544a

Browse files
committed
AST/Sema: Sink AvailabilityContext for location queries from Sema to AST.
1 parent 151c9de commit 2dd544a

9 files changed

+87
-89
lines changed

include/swift/AST/AvailabilityContext.h

+13
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
namespace swift {
2929
class ASTContext;
3030
class AvailableAttr;
31+
class AvailabilityScope;
3132
class Decl;
33+
class DeclContext;
3234

3335
/// An `AvailabilityContext` summarizes the availability constraints for a
3436
/// specific scope, such as within a declaration or at a particular source
@@ -62,6 +64,17 @@ class AvailabilityContext {
6264
/// set to the deployment target.
6365
static AvailabilityContext forDeploymentTarget(const ASTContext &ctx);
6466

67+
/// Returns the most refined `AvailabilityContext` for the given source
68+
/// location. If `refinedScope` is not `nullptr`, it will be set to the most
69+
/// refined scope that contains the location.
70+
static AvailabilityContext
71+
forLocation(SourceLoc loc, const DeclContext *declContext,
72+
const AvailabilityScope **refinedScope = nullptr);
73+
74+
/// Returns the availability context of the signature (rather than the body)
75+
/// of the given declaration.
76+
static AvailabilityContext forDeclSignature(const Decl *decl);
77+
6578
/// Returns the range of platform versions which may execute code in the
6679
/// availability context, starting at its introduction version.
6780
// FIXME: [availability] Remove; superseded by getAvailableRange().

lib/AST/AvailabilityContext.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include "swift/AST/AvailabilityConstraint.h"
1616
#include "swift/AST/AvailabilityContextStorage.h"
1717
#include "swift/AST/AvailabilityInference.h"
18+
#include "swift/AST/AvailabilityScope.h"
1819
#include "swift/AST/Decl.h"
20+
#include "swift/AST/Module.h"
1921
#include "swift/Basic/Assertions.h"
2022

2123
using namespace swift;
@@ -153,6 +155,65 @@ AvailabilityContext::forDeploymentTarget(const ASTContext &ctx) {
153155
AvailabilityRange::forDeploymentTarget(ctx), ctx);
154156
}
155157

158+
AvailabilityContext
159+
AvailabilityContext::forLocation(SourceLoc loc, const DeclContext *declContext,
160+
const AvailabilityScope **refinedScope) {
161+
auto &ctx = declContext->getASTContext();
162+
SourceFile *sf =
163+
loc.isValid()
164+
? declContext->getParentModule()->getSourceFileContainingLocation(loc)
165+
: declContext->getParentSourceFile();
166+
167+
// If our source location is invalid (this may be synthesized code), climb the
168+
// decl context hierarchy until we find a location that is valid, merging
169+
// availability contexts on the way up.
170+
//
171+
// Because we are traversing decl contexts, we will miss availability scopes
172+
// in synthesized code that are introduced by AST elements that are themselves
173+
// not decl contexts, such as `#available(..)` and property declarations.
174+
// Therefore a reference with an invalid source location that is contained
175+
// inside an `#available()` and with no intermediate decl context will not be
176+
// refined. For now, this is fine, but if we ever synthesize #available(),
177+
// this could become a problem..
178+
179+
// We can assume we are running on at least the minimum inlining target.
180+
auto baseAvailability = AvailabilityContext::forInliningTarget(ctx);
181+
auto isInvalidLoc = [sf](SourceLoc loc) {
182+
return sf ? loc.isInvalid() : true;
183+
};
184+
while (declContext && isInvalidLoc(loc)) {
185+
const Decl *decl = declContext->getInnermostDeclarationDeclContext();
186+
if (!decl)
187+
break;
188+
189+
baseAvailability.constrainWithDecl(decl);
190+
loc = decl->getLoc();
191+
declContext = decl->getDeclContext();
192+
}
193+
194+
if (!sf || loc.isInvalid())
195+
return baseAvailability;
196+
197+
auto *rootScope = AvailabilityScope::getOrBuildForSourceFile(*sf);
198+
if (!rootScope)
199+
return baseAvailability;
200+
201+
auto *scope = rootScope->findMostRefinedSubContext(loc, ctx);
202+
if (!scope)
203+
return baseAvailability;
204+
205+
if (refinedScope)
206+
*refinedScope = scope;
207+
208+
auto availability = scope->getAvailabilityContext();
209+
availability.constrainWithContext(baseAvailability, ctx);
210+
return availability;
211+
}
212+
213+
AvailabilityContext AvailabilityContext::forDeclSignature(const Decl *decl) {
214+
return forLocation(decl->getLoc(), decl->getInnermostDeclContext());
215+
}
216+
156217
AvailabilityRange AvailabilityContext::getPlatformRange() const {
157218
return storage->platformRange;
158219
}

lib/Sema/ConstraintSystem.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -4588,7 +4588,7 @@ Expr *ConstraintSystem::buildTypeErasedExpr(Expr *expr, DeclContext *dc,
45884588
auto *PD = protocols.front();
45894589

45904590
auto contextAvailability =
4591-
TypeChecker::availabilityAtLocation(expr->getLoc(), dc);
4591+
AvailabilityContext::forLocation(expr->getLoc(), dc);
45924592
auto refinedAvailability =
45934593
AvailabilityContext::forPlatformRange(
45944594
AvailabilityRange::alwaysAvailable(), ctx);
@@ -4600,8 +4600,7 @@ Expr *ConstraintSystem::buildTypeErasedExpr(Expr *expr, DeclContext *dc,
46004600
assert(eraser && "Failed to resolve eraser type!");
46014601

46024602
auto *nominal = eraser->getAnyNominal();
4603-
auto nominalAvailability =
4604-
TypeChecker::availabilityForDeclSignature(nominal);
4603+
auto nominalAvailability = AvailabilityContext::forDeclSignature(nominal);
46054604

46064605
if (contextAvailability.isContainedIn(nominalAvailability) &&
46074606
nominalAvailability.isContainedIn(refinedAvailability)) {

lib/Sema/TypeCheckAttr.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -5089,7 +5089,7 @@ void AttributeChecker::checkAvailableAttrs(ArrayRef<AvailableAttr *> attrs) {
50895089
auto availabilityContext = AvailabilityContext::forDeploymentTarget(Ctx);
50905090
if (auto parent =
50915091
AvailabilityInference::parentDeclForInferredAvailability(D)) {
5092-
auto parentAvailability = TypeChecker::availabilityForDeclSignature(parent);
5092+
auto parentAvailability = AvailabilityContext::forDeclSignature(parent);
50935093
availabilityContext.constrainWithContext(parentAvailability, Ctx);
50945094
}
50955095

@@ -5235,7 +5235,7 @@ void AttributeChecker::checkBackDeployedAttrs(
52355235
if (Ctx.LangOpts.DisableAvailabilityChecking)
52365236
continue;
52375237

5238-
auto availability = TypeChecker::availabilityAtLocation(
5238+
auto availability = AvailabilityContext::forLocation(
52395239
D->getLoc(), D->getInnermostDeclContext());
52405240

52415241
// Unavailable decls cannot be back deployed.

lib/Sema/TypeCheckAvailability.cpp

+5-70
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ ExportContext ExportContext::forDeclSignature(Decl *D) {
169169
auto *DC = D->getInnermostDeclContext();
170170
auto fragileKind = DC->getFragileFunctionKind();
171171
auto loc = D->getLoc();
172-
auto availabilityContext = TypeChecker::availabilityAtLocation(loc, DC);
172+
auto availabilityContext = AvailabilityContext::forLocation(loc, DC);
173173
bool spi = Ctx.LangOpts.LibraryLevel == LibraryLevel::SPI;
174174
bool implicit = false;
175175
computeExportContextBits(Ctx, D, &spi, &implicit);
@@ -187,7 +187,7 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC, SourceLoc loc) {
187187
auto &Ctx = DC->getASTContext();
188188

189189
auto fragileKind = DC->getFragileFunctionKind();
190-
auto availabilityContext = TypeChecker::availabilityAtLocation(loc, DC);
190+
auto availabilityContext = AvailabilityContext::forLocation(loc, DC);
191191
bool spi = Ctx.LangOpts.LibraryLevel == LibraryLevel::SPI;
192192
bool implicit = false;
193193
forEachOuterDecl(
@@ -288,75 +288,11 @@ static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) {
288288
return DC->isAsyncContext();
289289
}
290290

291-
AvailabilityContext
292-
TypeChecker::availabilityAtLocation(SourceLoc loc, const DeclContext *DC,
293-
const AvailabilityScope **MostRefined) {
294-
SourceFile *SF;
295-
if (loc.isValid())
296-
SF = DC->getParentModule()->getSourceFileContainingLocation(loc);
297-
else
298-
SF = DC->getParentSourceFile();
299-
auto &Context = DC->getASTContext();
300-
301-
// If our source location is invalid (this may be synthesized code), climb
302-
// the decl context hierarchy until we find a location that is valid,
303-
// collecting availability ranges on the way up.
304-
// We will combine the version ranges from these annotations
305-
// with the scope for the valid location to overapproximate the running
306-
// OS versions at the original source location.
307-
// Because we are climbing DeclContexts we will miss availability scopes in
308-
// synthesized code that are introduced by AST elements that are themselves
309-
// not DeclContexts, such as #available(..) and property declarations.
310-
// That is, a reference with an invalid location that is contained
311-
// inside a #available() and with no intermediate DeclContext will not be
312-
// refined. For now, this is fine -- but if we ever synthesize #available(),
313-
// this will be a real problem.
314-
315-
// We can assume we are running on at least the minimum inlining target.
316-
auto baseAvailability = AvailabilityContext::forInliningTarget(Context);
317-
auto isInvalidLoc = [SF](SourceLoc loc) {
318-
return SF ? loc.isInvalid() : true;
319-
};
320-
while (DC && isInvalidLoc(loc)) {
321-
const Decl *D = DC->getInnermostDeclarationDeclContext();
322-
if (!D)
323-
break;
324-
325-
baseAvailability.constrainWithDecl(D);
326-
loc = D->getLoc();
327-
DC = D->getDeclContext();
328-
}
329-
330-
if (!SF || loc.isInvalid())
331-
return baseAvailability;
332-
333-
auto *rootScope = AvailabilityScope::getOrBuildForSourceFile(*SF);
334-
if (!rootScope)
335-
return baseAvailability;
336-
337-
AvailabilityScope *scope = rootScope->findMostRefinedSubContext(loc, Context);
338-
if (!scope)
339-
return baseAvailability;
340-
341-
if (MostRefined) {
342-
*MostRefined = scope;
343-
}
344-
345-
auto availability = scope->getAvailabilityContext();
346-
availability.constrainWithContext(baseAvailability, Context);
347-
return availability;
348-
}
349-
350-
AvailabilityContext
351-
TypeChecker::availabilityForDeclSignature(const Decl *decl) {
352-
return TypeChecker::availabilityAtLocation(decl->getLoc(),
353-
decl->getInnermostDeclContext());
354-
}
355-
356291
AvailabilityRange TypeChecker::overApproximateAvailabilityAtLocation(
357292
SourceLoc loc, const DeclContext *DC,
358293
const AvailabilityScope **MostRefined) {
359-
return availabilityAtLocation(loc, DC, MostRefined).getPlatformRange();
294+
return AvailabilityContext::forLocation(loc, DC, MostRefined)
295+
.getPlatformRange();
360296
}
361297

362298
/// A class that walks the AST to find the innermost (i.e., deepest) node that
@@ -1831,8 +1767,7 @@ swift::getUnsatisfiedAvailabilityConstraint(const Decl *decl,
18311767
const DeclContext *referenceDC,
18321768
SourceLoc referenceLoc) {
18331769
return getAvailabilityConstraintsForDecl(
1834-
decl,
1835-
TypeChecker::availabilityAtLocation(referenceLoc, referenceDC))
1770+
decl, AvailabilityContext::forLocation(referenceLoc, referenceDC))
18361771
.getPrimaryConstraint();
18371772
}
18381773

lib/Sema/TypeCheckDecl.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1688,8 +1688,8 @@ bool TypeChecker::isAvailabilitySafeForConformance(
16881688
assert(dc->getSelfNominalTypeDecl() &&
16891689
"Must have a nominal or extension context");
16901690

1691-
AvailabilityContext contextForConformingDecl =
1692-
availabilityForDeclSignature(dc->getAsDecl());
1691+
auto contextForConformingDecl =
1692+
AvailabilityContext::forDeclSignature(dc->getAsDecl());
16931693

16941694
// If the conformance is unavailable then it's irrelevant whether the witness
16951695
// is potentially unavailable.

lib/Sema/TypeCheckDeclOverride.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1824,7 +1824,7 @@ static bool isAvailabilitySafeForOverride(ValueDecl *override,
18241824

18251825
// Allow overrides that are not as available as the base decl as long as the
18261826
// override is as available as its context.
1827-
auto availabilityContext = TypeChecker::availabilityForDeclSignature(
1827+
auto availabilityContext = AvailabilityContext::forDeclSignature(
18281828
override->getDeclContext()->getSelfNominalTypeDecl());
18291829

18301830
return availabilityContext.getPlatformRange().isContainedIn(overrideInfo);

lib/Sema/TypeCheckStorage.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2694,7 +2694,7 @@ bool AbstractStorageDecl::requiresCorrespondingUnderscoredCoroutineAccessor(
26942694
if (!ctx.supportsVersionedAvailability())
26952695
return true;
26962696

2697-
auto modifyAvailability = TypeChecker::availabilityAtLocation({}, accessor);
2697+
auto modifyAvailability = AvailabilityContext::forLocation({}, accessor);
26982698
auto featureAvailability = ctx.getCoroutineAccessorsRuntimeAvailability();
26992699
// If accessor was introduced only after the feature was, there's no old ABI
27002700
// to maintain.

lib/Sema/TypeChecker.h

-10
Original file line numberDiff line numberDiff line change
@@ -1031,16 +1031,6 @@ bool isAvailabilitySafeForConformance(
10311031
const ValueDecl *witness, const DeclContext *dc,
10321032
AvailabilityRange &requiredAvailability);
10331033

1034-
/// Returns the most refined `AvailabilityContext` for the given location.
1035-
/// If `MostRefined` is not `nullptr`, it will be set to the most refined scope
1036-
/// that contains the given location.
1037-
AvailabilityContext
1038-
availabilityAtLocation(SourceLoc loc, const DeclContext *DC,
1039-
const AvailabilityScope **MostRefined = nullptr);
1040-
1041-
/// Returns the availability context of the signature of the given declaration.
1042-
AvailabilityContext availabilityForDeclSignature(const Decl *decl);
1043-
10441034
/// Returns an over-approximation of the range of operating system versions
10451035
/// that could the passed-in location could be executing upon for
10461036
/// the target platform. If MostRefined != nullptr, set to the most-refined

0 commit comments

Comments
 (0)