Skip to content

Commit b33a71d

Browse files
committed
AST/Sema: Refactor lazy TypeRefinementContext expansion.
When building out the TypeRefinementContext tree, if we encounter an AST node that we cannot fully build out the children for, instead add a leaf node to the tree and mark it as needing expansion. Then skip walking the children of the AST node. When querying the tree, if when a node needing expansion is encountered, expand the children of the node on-demand. The previous implementation of lazy tree expansion did not actually skip the children of AST nodes requiring expansion, so the resulting TypeRefinementContext tree could contain malformed nodes with duplicate descendants.
1 parent fc43e46 commit b33a71d

File tree

6 files changed

+199
-55
lines changed

6 files changed

+199
-55
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4541,23 +4541,26 @@ class InitAccessorReferencedVariablesRequest
45414541
bool isCached() const { return true; }
45424542
};
45434543

4544-
/// Expand the children of the type refinement context for the given
4545-
/// declaration.
4544+
/// Expand the children of the given type refinement context.
45464545
class ExpandChildTypeRefinementContextsRequest
45474546
: public SimpleRequest<ExpandChildTypeRefinementContextsRequest,
4548-
bool(Decl *, TypeRefinementContext *),
4549-
RequestFlags::Cached> {
4547+
std::vector<TypeRefinementContext *>(
4548+
TypeRefinementContext *),
4549+
RequestFlags::SeparatelyCached> {
45504550
public:
45514551
using SimpleRequest::SimpleRequest;
45524552

45534553
private:
45544554
friend SimpleRequest;
45554555

4556-
bool evaluate(Evaluator &evaluator, Decl *decl,
4557-
TypeRefinementContext *parentTRC) const;
4556+
std::vector<TypeRefinementContext *>
4557+
evaluate(Evaluator &evaluator, TypeRefinementContext *parentTRC) const;
45584558

45594559
public:
4560+
// Separate caching.
45604561
bool isCached() const { return true; }
4562+
llvm::Optional<std::vector<TypeRefinementContext *>> getCachedResult() const;
4563+
void cacheResult(std::vector<TypeRefinementContext *> children) const;
45614564
};
45624565

45634566
class SerializeAttrGenericSignatureRequest

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,8 @@ SWIFT_REQUEST(TypeChecker, InitAccessorReferencedVariablesRequest,
513513
ArrayRef<Identifier>),
514514
Cached, NoLocationInfo)
515515
SWIFT_REQUEST(TypeChecker, ExpandChildTypeRefinementContextsRequest,
516-
bool(Decl *, TypeRefinementContext *),
517-
Cached, NoLocationInfo)
516+
std::vector<TypeRefinementContext *>(TypeRefinementContext *),
517+
SeparatelyCached, NoLocationInfo)
518518
SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
519519
GenericSignature(Decl *, SpecializeAttr *),
520520
SeparatelyCached, NoLocationInfo)

include/swift/AST/TypeRefinementContext.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
9797
};
9898

9999
private:
100+
friend class ExpandChildTypeRefinementContextsRequest;
100101

101102
/// Represents the AST node that introduced a refinement context.
102103
class IntroNode {
@@ -179,6 +180,11 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
179180

180181
std::vector<TypeRefinementContext *> Children;
181182

183+
struct {
184+
/// Whether this node has child nodes that have not yet been expanded.
185+
unsigned needsExpansion : 1;
186+
} LazyInfo = {};
187+
182188
TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
183189
TypeRefinementContext *Parent, SourceRange SrcRange,
184190
const AvailabilityContext &Info,
@@ -240,6 +246,13 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
240246
TypeRefinementContext *Parent,
241247
const AvailabilityContext &Info);
242248

249+
Decl *getDeclOrNull() const {
250+
auto IntroReason = getReason();
251+
if (IntroReason == Reason::Decl || IntroReason == Reason::DeclImplicit)
252+
return getIntroductionNode().getAsDecl();
253+
return nullptr;
254+
}
255+
243256
/// Returns the reason this context was introduced.
244257
Reason getReason() const;
245258

@@ -288,7 +301,13 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
288301
/// Returns the inner-most TypeRefinementContext descendant of this context
289302
/// for the given source location.
290303
TypeRefinementContext *findMostRefinedSubContext(SourceLoc Loc,
291-
SourceManager &SM);
304+
ASTContext &Ctx);
305+
306+
bool getNeedsExpansion() const { return LazyInfo.needsExpansion; }
307+
308+
void setNeedsExpansion(bool needsExpansion) {
309+
LazyInfo.needsExpansion = needsExpansion;
310+
}
292311

293312
SWIFT_DEBUG_DUMPER(dump(SourceManager &SrcMgr));
294313
void dump(raw_ostream &OS, SourceManager &SrcMgr) const;
@@ -300,6 +319,10 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
300319
void simple_display(llvm::raw_ostream &out,
301320
const TypeRefinementContext *trc);
302321

322+
inline SourceLoc extractNearestSourceLoc(const TypeRefinementContext *TRC) {
323+
return TRC->getIntroductionLoc();
324+
}
325+
303326
} // end namespace swift
304327

305328
#endif

lib/AST/TypeRefinementContext.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ TypeRefinementContext::createRoot(SourceFile *SF,
5757
auto charRange = Ctx.SourceMgr.getRangeForBuffer(*SF->getBufferID());
5858
range = SourceRange(charRange.getStart(), charRange.getEnd());
5959
parentContext = parentTRC->findMostRefinedSubContext(
60-
parentExpansion.getStartLoc(), Ctx.SourceMgr);
60+
parentExpansion.getStartLoc(), Ctx);
6161
availabilityContext = parentContext->getAvailabilityInfo();
6262
}
6363
}
@@ -186,29 +186,20 @@ static bool rangeContainsTokenLocWithGeneratedSource(
186186

187187
TypeRefinementContext *
188188
TypeRefinementContext::findMostRefinedSubContext(SourceLoc Loc,
189-
SourceManager &SM) {
189+
ASTContext &Ctx) {
190190
assert(Loc.isValid());
191-
191+
192192
if (SrcRange.isValid() &&
193-
!rangeContainsTokenLocWithGeneratedSource(SM, SrcRange, Loc))
193+
!rangeContainsTokenLocWithGeneratedSource(Ctx.SourceMgr, SrcRange, Loc))
194194
return nullptr;
195195

196-
// If this context is for a declaration, ensure that we've expanded the
197-
// children of the declaration.
198-
if (Node.getReason() == Reason::Decl ||
199-
Node.getReason() == Reason::DeclImplicit) {
200-
if (auto decl = Node.getAsDecl()) {
201-
ASTContext &ctx = decl->getASTContext();
202-
(void)evaluateOrDefault(
203-
ctx.evaluator, ExpandChildTypeRefinementContextsRequest{decl, this},
204-
false);
205-
}
206-
}
196+
auto expandedChildren = evaluateOrDefault(
197+
Ctx.evaluator, ExpandChildTypeRefinementContextsRequest{this}, {});
207198

208199
// For the moment, we perform a linear search here, but we can and should
209200
// do something more efficient.
210-
for (TypeRefinementContext *Child : Children) {
211-
if (auto *Found = Child->findMostRefinedSubContext(Loc, SM)) {
201+
for (TypeRefinementContext *Child : expandedChildren) {
202+
if (auto *Found = Child->findMostRefinedSubContext(Loc, Ctx)) {
212203
return Found;
213204
}
214205
}
@@ -433,3 +424,18 @@ void swift::simple_display(
433424
llvm::raw_ostream &out, const TypeRefinementContext *trc) {
434425
out << "TRC @" << trc;
435426
}
427+
428+
llvm::Optional<std::vector<TypeRefinementContext *>>
429+
ExpandChildTypeRefinementContextsRequest::getCachedResult() const {
430+
auto *TRC = std::get<0>(getStorage());
431+
if (TRC->getNeedsExpansion())
432+
return llvm::None;
433+
return TRC->Children;
434+
}
435+
436+
void ExpandChildTypeRefinementContextsRequest::cacheResult(
437+
std::vector<TypeRefinementContext *> children) const {
438+
auto *TRC = std::get<0>(getStorage());
439+
TRC->Children = children;
440+
TRC->setNeedsExpansion(false);
441+
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,17 @@ class TypeRefinementContextBuilder : private ASTWalker {
472472
PreWalkAction walkToDeclPre(Decl *D) override {
473473
PrettyStackTraceDecl trace(stackTraceAction(), D);
474474

475+
// Implicit decls don't have source locations so they cannot have a TRC.
476+
if (D->isImplicit())
477+
return Action::Continue();
478+
479+
// The AST of this decl may not be ready to traverse yet if it hasn't been
480+
// full typechecked. If that's the case, we leave a placeholder node in the
481+
// tree to indicate that the subtree should be expanded lazily when it
482+
// needs to be traversed.
483+
if (buildLazyContextForDecl(D))
484+
return Action::SkipChildren();
485+
475486
// Adds in a TRC that covers the entire declaration.
476487
if (auto DeclTRC = getNewContextForSignatureOfDecl(D)) {
477488
pushContext(DeclTRC, D);
@@ -497,6 +508,44 @@ class TypeRefinementContextBuilder : private ASTWalker {
497508
return Action::Continue();
498509
}
499510

511+
/// Constructs a placeholder TRC node that should be expanded later if the AST
512+
/// associated with the given declaration is not ready to be traversed yet.
513+
/// Returns true if a node was created.
514+
bool buildLazyContextForDecl(Decl *D) {
515+
if (!isa<PatternBindingDecl>(D))
516+
return false;
517+
518+
// Check whether the current TRC is already a lazy placeholder. If it is,
519+
// we should try to expand it rather than creating a new placeholder.
520+
auto currentTRC = getCurrentTRC();
521+
if (currentTRC->getNeedsExpansion() && currentTRC->getDeclOrNull() == D)
522+
return false;
523+
524+
// Pattern binding declarations may have attached property wrappers that
525+
// get expanded from macros attached to the parent declaration. We must
526+
// not eagerly expand the attached property wrappers to avoid a request
527+
// cycle.
528+
if (auto *pattern = dyn_cast<PatternBindingDecl>(D)) {
529+
if (auto firstVar = pattern->getAnchoringVarDecl(0)) {
530+
// If there's no initial value, or the init is exposed to clients, then
531+
// we don't need to create any implicit TRCs for the init bodies.
532+
if (!firstVar->hasInitialValue() || firstVar->isInitExposedToClients())
533+
return false;
534+
535+
// FIXME: We could narrow this further by detecting whether there are
536+
// any macro expansions required to visit the CustomAttrs of the var.
537+
}
538+
}
539+
540+
// If we've made it this far then we've identified a declaration that
541+
// requires lazy expansion later.
542+
auto lazyTRC = TypeRefinementContext::createForDeclImplicit(
543+
Context, D, currentTRC, currentTRC->getAvailabilityInfo(),
544+
refinementSourceRangeForDecl(D));
545+
lazyTRC->setNeedsExpansion(true);
546+
return true;
547+
}
548+
500549
/// Returns a new context to be introduced for the declaration, or nullptr
501550
/// if no new context should be introduced.
502551
TypeRefinementContext *getNewContextForSignatureOfDecl(Decl *D) {
@@ -519,10 +568,6 @@ class TypeRefinementContextBuilder : private ASTWalker {
519568
return nullptr;
520569
}
521570

522-
// Ignore implicit declarations (mainly skips over `DeferStmt` functions).
523-
if (D->isImplicit())
524-
return nullptr;
525-
526571
// Declarations with an explicit availability attribute always get a TRC.
527572
if (hasActiveAvailableAttribute(D, Context)) {
528573
AvailabilityContext DeclaredAvailability =
@@ -539,17 +584,11 @@ class TypeRefinementContextBuilder : private ASTWalker {
539584
// internal property in a public struct can be effectively less available
540585
// than the containing struct decl because the internal property will only
541586
// be accessed by code running at the deployment target or later.
542-
//
543-
// For declarations that have their child construction delayed, always
544-
// create this implicit declaration context. It will be used to trigger
545-
// lazy creation of the child TRCs.
546587
AvailabilityContext CurrentAvailability =
547588
getCurrentTRC()->getAvailabilityInfo();
548589
AvailabilityContext EffectiveAvailability =
549590
getEffectiveAvailabilityForDeclSignature(D, CurrentAvailability);
550-
if ((isa<PatternBindingDecl>(D) &&
551-
refinementSourceRangeForDecl(D).isValid()) ||
552-
CurrentAvailability.isSupersetOf(EffectiveAvailability))
591+
if (CurrentAvailability.isSupersetOf(EffectiveAvailability))
553592
return TypeRefinementContext::createForDeclImplicit(
554593
Context, D, getCurrentTRC(), EffectiveAvailability,
555594
refinementSourceRangeForDecl(D));
@@ -732,6 +771,11 @@ class TypeRefinementContextBuilder : private ASTWalker {
732771
}
733772
return;
734773
}
774+
775+
if (auto pattern = dyn_cast<PatternBindingDecl>(D)) {
776+
buildContextsForPatternBindingDecl(pattern);
777+
return;
778+
}
735779
}
736780

737781
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
@@ -1208,7 +1252,7 @@ void TypeChecker::buildTypeRefinementContextHierarchyDelayed(SourceFile &SF, Abs
12081252

12091253
// Build the refinement context for the function body.
12101254
ASTContext &Context = SF.getASTContext();
1211-
auto LocalTRC = RootTRC->findMostRefinedSubContext(AFD->getLoc(), Context.SourceMgr);
1255+
auto LocalTRC = RootTRC->findMostRefinedSubContext(AFD->getLoc(), Context);
12121256
TypeRefinementContextBuilder Builder(LocalTRC, Context);
12131257
Builder.build(AFD);
12141258
}
@@ -1224,23 +1268,16 @@ TypeChecker::getOrBuildTypeRefinementContext(SourceFile *SF) {
12241268
return TRC;
12251269
}
12261270

1227-
bool ExpandChildTypeRefinementContextsRequest::evaluate(
1228-
Evaluator &evaluator, Decl *decl, TypeRefinementContext *parentTRC
1229-
) const {
1230-
// If the parent TRC is already contained by the deployment target, there's
1231-
// nothing more to do.
1232-
ASTContext &ctx = decl->getASTContext();
1233-
if (computeContainedByDeploymentTarget(parentTRC, ctx))
1234-
return false;
1235-
1236-
// Pattern binding declarations can have children corresponding to property
1237-
// wrappers and the initial values provided in each pattern binding entry.
1238-
if (auto *binding = dyn_cast<PatternBindingDecl>(decl)) {
1271+
std::vector<TypeRefinementContext *>
1272+
ExpandChildTypeRefinementContextsRequest::evaluate(
1273+
Evaluator &evaluator, TypeRefinementContext *parentTRC) const {
1274+
assert(parentTRC->getNeedsExpansion());
1275+
if (auto decl = parentTRC->getDeclOrNull()) {
1276+
ASTContext &ctx = decl->getASTContext();
12391277
TypeRefinementContextBuilder builder(parentTRC, ctx);
1240-
builder.buildContextsForPatternBindingDecl(binding);
1278+
builder.build(decl);
12411279
}
1242-
1243-
return false;
1280+
return parentTRC->Children;
12441281
}
12451282

12461283
AvailabilityContext
@@ -1293,7 +1330,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc,
12931330
if (SF && loc.isValid()) {
12941331
TypeRefinementContext *rootTRC = getOrBuildTypeRefinementContext(SF);
12951332
TypeRefinementContext *TRC =
1296-
rootTRC->findMostRefinedSubContext(loc, Context.SourceMgr);
1333+
rootTRC->findMostRefinedSubContext(loc, Context);
12971334
OverApproximateContext.constrainWith(TRC->getAvailabilityInfo());
12981335
if (MostRefined) {
12991336
*MostRefined = TRC;

0 commit comments

Comments
 (0)