Skip to content

Commit a5004b7

Browse files
committed
WIP rdar://92713589
1 parent 54a11df commit a5004b7

File tree

2 files changed

+205
-171
lines changed

2 files changed

+205
-171
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 89 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/Initializer.h"
2525
#include "swift/AST/NameLookup.h"
2626
#include "swift/AST/Pattern.h"
27+
#include "swift/AST/PrettyStackTrace.h"
2728
#include "swift/AST/ProtocolConformance.h"
2829
#include "swift/AST/SourceFile.h"
2930
#include "swift/AST/TypeDeclFinder.h"
@@ -320,21 +321,6 @@ static bool hasActiveAvailableAttribute(Decl *D,
320321
return getActiveAvailableAttribute(D, AC);
321322
}
322323

323-
static bool shouldConstrainBodyToDeploymentTarget(Decl *D) {
324-
// The declaration contains code...
325-
if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
326-
// And it has a location so we can check it...
327-
if (!afd->isImplicit() && afd->getBodySourceRange().isValid()) {
328-
// And the code is within our resilience domain, so it should be
329-
// compiled with the minimum deployment target, not the minimum inlining
330-
// target.
331-
return afd->getResilienceExpansion() != ResilienceExpansion::Minimal;
332-
}
333-
}
334-
335-
return false;
336-
}
337-
338324
static bool computeContainedByDeploymentTarget(TypeRefinementContext *TRC,
339325
ASTContext &ctx) {
340326
return TRC->getAvailabilityInfo()
@@ -418,6 +404,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
418404
ContextStack.push_back(Info);
419405
}
420406

407+
const char *stackTraceAction() const {
408+
return "building type refinement context";
409+
}
410+
421411
public:
422412
TypeRefinementContextBuilder(TypeRefinementContext *TRC, ASTContext &Context)
423413
: Context(Context) {
@@ -426,20 +416,23 @@ class TypeRefinementContextBuilder : private ASTWalker {
426416
}
427417

428418
void build(Decl *D) {
419+
PrettyStackTraceDecl trace(stackTraceAction(), D);
429420
unsigned StackHeight = ContextStack.size();
430421
D->walk(*this);
431422
assert(ContextStack.size() == StackHeight);
432423
(void)StackHeight;
433424
}
434425

435426
void build(Stmt *S) {
427+
PrettyStackTraceStmt trace(Context, stackTraceAction(), S);
436428
unsigned StackHeight = ContextStack.size();
437429
S->walk(*this);
438430
assert(ContextStack.size() == StackHeight);
439431
(void)StackHeight;
440432
}
441433

442434
void build(Expr *E) {
435+
PrettyStackTraceExpr trace(Context, stackTraceAction(), E);
443436
unsigned StackHeight = ContextStack.size();
444437
E->walk(*this);
445438
assert(ContextStack.size() == StackHeight);
@@ -448,6 +441,8 @@ class TypeRefinementContextBuilder : private ASTWalker {
448441

449442
private:
450443
bool walkToDeclPre(Decl *D) override {
444+
PrettyStackTraceDecl trace(stackTraceAction(), D);
445+
451446
// Adds in a parent TRC for decls which are syntactically nested but are not
452447
// represented that way in the AST. (Particularly, AbstractStorageDecl
453448
// parents for AccessorDecl children.)
@@ -460,17 +455,12 @@ class TypeRefinementContextBuilder : private ASTWalker {
460455
pushContext(DeclTRC, D);
461456
}
462457

463-
// Adds in a TRC that covers only the body of the declaration.
464-
if (auto BodyTRC = getNewContextForBodyOfDecl(D)) {
465-
pushContext(BodyTRC, D);
466-
}
467-
458+
// Create TRCs that cover only the body of the declaration.
459+
buildContextsForBodyOfDecl(D);
468460
return true;
469461
}
470462

471463
bool walkToDeclPost(Decl *D) override {
472-
// As seen above, we could have up to three TRCs in the stack for a single
473-
// declaration.
474464
while (ContextStack.back().ScopeNode.getAsDecl() == D) {
475465
ContextStack.pop_back();
476466
}
@@ -515,7 +505,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
515505
return nullptr;
516506
}
517507

518-
/// Builds the type refinement hierarchy for the body of the function.
508+
/// Builds the type refinement hierarchy for the signature of the declaration.
519509
TypeRefinementContext *buildDeclarationRefinementContext(Decl *D) {
520510
// We require a valid range in order to be able to query for the TRC
521511
// corresponding to a given SourceLoc.
@@ -611,15 +601,19 @@ class TypeRefinementContextBuilder : private ASTWalker {
611601
if (auto *storageDecl = dyn_cast<AbstractStorageDecl>(D)) {
612602
// Use the declaration's availability for the context when checking
613603
// the bodies of its accessors.
604+
SourceRange Range = storageDecl->getSourceRange();
614605

615-
Decl *locDecl = D;
616606
// For a variable declaration (without accessors) we use the range of the
617607
// containing pattern binding declaration to make sure that we include
618-
// any type annotation in the type refinement context range.
619-
if (auto varDecl = dyn_cast<VarDecl>(storageDecl)) {
620-
auto *PBD = varDecl->getParentPatternBinding();
621-
if (PBD)
622-
locDecl = PBD;
608+
// any type annotation in the type refinement context range. We also
609+
// need to include any attached property wrappers.
610+
if (auto *varDecl = dyn_cast<VarDecl>(storageDecl)) {
611+
if (auto *PBD = varDecl->getParentPatternBinding())
612+
Range = PBD->getSourceRange();
613+
614+
for (auto *propertyWrapper : varDecl->getAttachedPropertyWrappers()) {
615+
Range.widen(propertyWrapper->getRange());
616+
}
623617
}
624618

625619
// HACK: For synthesized trivial accessors we may have not a valid
@@ -628,58 +622,91 @@ class TypeRefinementContextBuilder : private ASTWalker {
628622
// to update AbstractStorageDecl::addTrivialAccessors() to take brace
629623
// locations and have callers of that method provide appropriate source
630624
// locations.
631-
SourceLoc BracesEnd = storageDecl->getBracesRange().End;
632-
if (storageDecl->hasParsedAccessors() && BracesEnd.isValid()) {
633-
return SourceRange(locDecl->getStartLoc(),
634-
BracesEnd);
625+
SourceRange BracesRange = storageDecl->getBracesRange();
626+
if (storageDecl->hasParsedAccessors() && BracesRange.isValid()) {
627+
Range.widen(BracesRange);
635628
}
636629

637-
return locDecl->getSourceRange();
630+
return Range;
638631
}
639632

640633
return D->getSourceRange();
641634
}
642635

643-
TypeRefinementContext *getNewContextForBodyOfDecl(Decl *D) {
644-
if (bodyIntroducesNewContext(D))
645-
return buildBodyRefinementContext(D);
636+
TypeRefinementContext *createAPIBoundaryContext(Decl *D, SourceRange range) {
637+
AvailabilityContext DeploymentTargetInfo =
638+
AvailabilityContext::forDeploymentTarget(Context);
639+
DeploymentTargetInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo());
646640

647-
return nullptr;
641+
return TypeRefinementContext::createForAPIBoundary(
642+
Context, D, getCurrentTRC(), DeploymentTargetInfo, range);
648643
}
649644

650-
bool bodyIntroducesNewContext(Decl *D) {
651-
// Are we already constrained by the deployment target? If not, adding a
652-
// new context wouldn't change availability.
645+
void buildContextsForBodyOfDecl(Decl *D) {
646+
// Are we already constrained by the deployment target? If not, adding
647+
// new contexts won't change availability.
653648
if (isCurrentTRCContainedByDeploymentTarget())
654-
return false;
655-
656-
// If we're in a function, check if it ought to use the deployment target.
657-
if (auto afd = dyn_cast<AbstractFunctionDecl>(D))
658-
return shouldConstrainBodyToDeploymentTarget(afd);
659-
660-
// The only other case we care about is top-level code.
661-
return isa<TopLevelCodeDecl>(D);
662-
}
649+
return;
663650

664-
TypeRefinementContext *buildBodyRefinementContext(Decl *D) {
665-
SourceRange range;
651+
// Top level code always uses the deployment target.
666652
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
667-
range = tlcd->getSourceRange();
668-
} else if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
669-
range = afd->getBodySourceRange();
670-
} else {
671-
llvm_unreachable("unknown decl");
653+
pushContext(createAPIBoundaryContext(tlcd, tlcd->getSourceRange()), D);
654+
return;
672655
}
673656

674-
AvailabilityContext DeploymentTargetInfo =
675-
AvailabilityContext::forDeploymentTarget(Context);
676-
DeploymentTargetInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo());
657+
// Function bodies use the deployment target if they are within the module's
658+
// resilience domain.
659+
if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
660+
if (!afd->isImplicit() && afd->getBodySourceRange().isValid() &&
661+
afd->getResilienceExpansion() != ResilienceExpansion::Minimal) {
662+
pushContext(createAPIBoundaryContext(afd, afd->getBodySourceRange()),
663+
D);
664+
}
665+
return;
666+
}
677667

678-
return TypeRefinementContext::createForAPIBoundary(
679-
Context, D, getCurrentTRC(), DeploymentTargetInfo, range);
668+
// Var decls may have associated pattern binding decls or property wrappers
669+
// with init expressions. Those expressions need to be constrained to the
670+
// deployment target unless they are exposed to clients.
671+
if (auto vd = dyn_cast<VarDecl>(D)) {
672+
if (!vd->hasInitialValue() || vd->isInitExposedToClients())
673+
return;
674+
675+
if (auto *pbd = vd->getParentPatternBinding()) {
676+
int idx = pbd->getPatternEntryIndexForVarDecl(vd);
677+
auto *initExpr = pbd->getInit(idx);
678+
if (initExpr && !initExpr->isImplicit()) {
679+
assert(initExpr->getSourceRange().isValid());
680+
681+
// Create a TRC for the init written in the source. The ASTWalker
682+
// won't visit these expressions so instead of pushing these onto the
683+
// stack we build them directly.
684+
auto *initTRC =
685+
createAPIBoundaryContext(vd, initExpr->getSourceRange());
686+
TypeRefinementContextBuilder(initTRC, Context).build(initExpr);
687+
}
688+
689+
// Ideally any init expression would be returned by `getInit()` above.
690+
// However, for property wrappers it doesn't get populated until
691+
// typechecking completes (which is too late). Instead, we find the
692+
// the property wrapper attribute use its source range to create a TRC
693+
// for the initializer expression.
694+
// FIXME: Since we don't have an expression here, we can't build out its
695+
// TRC. Currently that doesn't matter because expression contents don't
696+
// ever contribute to the TRC, but it could become relevant if we ever
697+
// get control flow expressions.
698+
for (auto *wrapper : vd->getAttachedPropertyWrappers()) {
699+
createAPIBoundaryContext(vd, wrapper->getRange());
700+
}
701+
}
702+
703+
return;
704+
}
680705
}
681706

682707
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
708+
PrettyStackTraceStmt trace(Context, stackTraceAction(), S);
709+
683710
if (auto *IS = dyn_cast<IfStmt>(S)) {
684711
buildIfStmtRefinementContext(IS);
685712
return std::make_pair(false, S);

0 commit comments

Comments
 (0)