Skip to content

Commit cee810a

Browse files
authored
Merge pull request #76912 from tshortli/sorted-type-refinement-contexts
AST: Sort TypeRefinementContexts for better lookup performance
2 parents 6d879fb + 42ebee8 commit cee810a

File tree

4 files changed

+172
-53
lines changed

4 files changed

+172
-53
lines changed

include/swift/AST/TypeRefinementContext.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
185185
unsigned needsExpansion : 1;
186186
} LazyInfo = {};
187187

188+
void verify(const TypeRefinementContext *parent, ASTContext &ctx) const;
189+
188190
TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
189191
TypeRefinementContext *Parent, SourceRange SrcRange,
190192
const AvailabilityRange &Info,
@@ -290,10 +292,7 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
290292
}
291293

292294
/// Adds a child refinement context.
293-
void addChild(TypeRefinementContext *Child) {
294-
assert(Child->getSourceRange().isValid());
295-
Children.push_back(Child);
296-
}
295+
void addChild(TypeRefinementContext *Child, ASTContext &Ctx);
297296

298297
/// Returns the inner-most TypeRefinementContext descendant of this context
299298
/// for the given source location.
@@ -306,6 +305,10 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
306305
LazyInfo.needsExpansion = needsExpansion;
307306
}
308307

308+
/// Recursively check the tree for integrity. If any errors are found, emits
309+
/// diagnosticts to stderr and aborts.
310+
void verify(ASTContext &ctx);
311+
309312
SWIFT_DEBUG_DUMPER(dump(SourceManager &SrcMgr));
310313
void dump(raw_ostream &OS, SourceManager &SrcMgr) const;
311314
void print(raw_ostream &OS, SourceManager &SrcMgr, unsigned Indent = 0) const;

include/swift/Basic/SourceManager.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class GeneratedSourceInfo {
5555
enum Kind {
5656
#define MACRO_ROLE(Name, Description) Name##MacroExpansion,
5757
#include "swift/Basic/MacroRoles.def"
58+
#undef MACRO_ROLE
5859

5960
/// A new function body that is replacing an existing function body.
6061
ReplacedFunctionBody,
@@ -66,6 +67,23 @@ class GeneratedSourceInfo {
6667
DefaultArgument,
6768
} kind;
6869

70+
static StringRef kindToString(GeneratedSourceInfo::Kind kind) {
71+
switch (kind) {
72+
#define MACRO_ROLE(Name, Description) \
73+
case Name##MacroExpansion: \
74+
return #Name "MacroExpansion";
75+
#include "swift/Basic/MacroRoles.def"
76+
#undef MACRO_ROLE
77+
case ReplacedFunctionBody:
78+
return "ReplacedFunctionBody";
79+
case PrettyPrinted:
80+
return "PrettyPrinted";
81+
case DefaultArgument:
82+
return "DefaultArgument";
83+
}
84+
llvm_unreachable("Invalid kind");
85+
}
86+
6987
/// The source range in the enclosing buffer where this source was generated.
7088
///
7189
/// This could point at a macro expansion or at the implicit location at

lib/AST/ASTVerifier.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "swift/AST/SourceFile.h"
3737
#include "swift/AST/Stmt.h"
3838
#include "swift/AST/TypeCheckRequests.h"
39+
#include "swift/AST/TypeRefinementContext.h"
3940
#include "swift/AST/TypeRepr.h"
4041
#include "swift/Basic/Assertions.h"
4142
#include "swift/Basic/SourceManager.h"
@@ -3882,23 +3883,15 @@ void swift::verify(SourceFile &SF) {
38823883
return;
38833884
Verifier verifier(SF, &SF);
38843885
SF.walk(verifier);
3885-
}
38863886

3887-
bool swift::shouldVerify(const Decl *D, const ASTContext &Context) {
3888-
if (!shouldVerifyGivenContext(Context))
3889-
return false;
3890-
3891-
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
3892-
return shouldVerify(ED->getExtendedNominal(), Context);
3893-
}
3894-
3895-
const auto *VD = dyn_cast<ValueDecl>(D);
3896-
if (!VD) {
3897-
// Verify declarations without names everywhere.
3898-
return true;
3887+
// Verify the TypeRefinementContext hierarchy.
3888+
if (auto TRC = SF.getTypeRefinementContext()) {
3889+
TRC->verify(SF.getASTContext());
38993890
}
3891+
}
39003892

3901-
return true;
3893+
bool swift::shouldVerify(const Decl *D, const ASTContext &Context) {
3894+
return shouldVerifyGivenContext(Context);
39023895
}
39033896

39043897
void swift::verify(Decl *D) {

lib/AST/TypeRefinementContext.cpp

Lines changed: 140 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/AST/TypeRefinementContext.h"
18+
1719
#include "swift/AST/ASTContext.h"
1820
#include "swift/AST/Decl.h"
19-
#include "swift/AST/Module.h"
20-
#include "swift/AST/Stmt.h"
2121
#include "swift/AST/Expr.h"
22+
#include "swift/AST/Module.h"
2223
#include "swift/AST/SourceFile.h"
24+
#include "swift/AST/Stmt.h"
2325
#include "swift/AST/TypeCheckRequests.h"
24-
#include "swift/AST/TypeRefinementContext.h"
2526
#include "swift/Basic/Assertions.h"
2627
#include "swift/Basic/SourceManager.h"
2728

@@ -35,7 +36,7 @@ TypeRefinementContext::TypeRefinementContext(
3536
ExplicitAvailabilityInfo(ExplicitInfo) {
3637
if (Parent) {
3738
assert(SrcRange.isValid());
38-
Parent->addChild(this);
39+
Parent->addChild(this, Ctx);
3940
assert(Info.isContainedIn(Parent->getAvailabilityInfo()));
4041
}
4142
Ctx.addDestructorCleanup(Children);
@@ -169,52 +170,64 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S,
169170
Ctx, S, Parent, S->getBody()->getSourceRange(), Info, /* ExplicitInfo */Info);
170171
}
171172

172-
/// Determine whether the child location is somewhere within the parent
173-
/// range.
174-
static bool rangeContainsTokenLocWithGeneratedSource(
175-
SourceManager &sourceMgr, SourceRange parentRange, SourceLoc childLoc) {
176-
if (sourceMgr.rangeContainsTokenLoc(parentRange, childLoc))
177-
return true;
178-
179-
auto childBuffer = sourceMgr.findBufferContainingLoc(childLoc);
180-
while (!sourceMgr.rangeContainsTokenLoc(
181-
sourceMgr.getRangeForBuffer(childBuffer), parentRange.Start)) {
182-
auto *info = sourceMgr.getGeneratedSourceInfo(childBuffer);
183-
if (!info)
184-
return false;
185-
186-
childLoc = info->originalSourceRange.getStart();
187-
if (childLoc.isInvalid())
188-
return false;
189-
190-
childBuffer = sourceMgr.findBufferContainingLoc(childLoc);
173+
void TypeRefinementContext::addChild(TypeRefinementContext *Child,
174+
ASTContext &Ctx) {
175+
assert(Child->getSourceRange().isValid());
176+
177+
// Handle the first child.
178+
if (Children.empty()) {
179+
Children.push_back(Child);
180+
return;
191181
}
192182

193-
return sourceMgr.rangeContainsTokenLoc(parentRange, childLoc);
183+
// Handle a child that is ordered after the existing children (this should be
184+
// the common case).
185+
auto &srcMgr = Ctx.SourceMgr;
186+
if (srcMgr.isBefore(Children.back()->getSourceRange().Start,
187+
Child->getSourceRange().Start)) {
188+
Children.push_back(Child);
189+
return;
190+
}
191+
192+
// Insert the child amongst the existing sorted children.
193+
auto iter = std::upper_bound(
194+
Children.begin(), Children.end(), Child,
195+
[&srcMgr](TypeRefinementContext *lhs, TypeRefinementContext *rhs) {
196+
return srcMgr.isBefore(lhs->getSourceRange().Start,
197+
rhs->getSourceRange().Start);
198+
});
199+
200+
Children.insert(iter, Child);
194201
}
195202

196203
TypeRefinementContext *
197204
TypeRefinementContext::findMostRefinedSubContext(SourceLoc Loc,
198205
ASTContext &Ctx) {
199206
assert(Loc.isValid());
200207

201-
if (SrcRange.isValid() &&
202-
!rangeContainsTokenLocWithGeneratedSource(Ctx.SourceMgr, SrcRange, Loc))
208+
if (SrcRange.isValid() && !Ctx.SourceMgr.containsTokenLoc(SrcRange, Loc))
203209
return nullptr;
204210

205211
auto expandedChildren = evaluateOrDefault(
206212
Ctx.evaluator, ExpandChildTypeRefinementContextsRequest{this}, {});
207213

208-
// For the moment, we perform a linear search here, but we can and should
209-
// do something more efficient.
210-
for (TypeRefinementContext *Child : expandedChildren) {
211-
if (auto *Found = Child->findMostRefinedSubContext(Loc, Ctx)) {
212-
return Found;
213-
}
214+
// Do a binary search to find the first child with a source range that
215+
// ends after the given location.
216+
auto iter = std::lower_bound(
217+
expandedChildren.begin(), expandedChildren.end(), Loc,
218+
[&Ctx](TypeRefinementContext *context, SourceLoc loc) {
219+
return Ctx.SourceMgr.isBefore(context->getSourceRange().End, loc);
220+
});
221+
222+
// Check whether the matching child or any of its descendants contain
223+
// the given location.
224+
if (iter != expandedChildren.end()) {
225+
if (auto found = (*iter)->findMostRefinedSubContext(Loc, Ctx))
226+
return found;
214227
}
215228

216-
// Loc is in this context's range but not in any child's, so this context
217-
// must be the inner-most context.
229+
// The location is in this context's range but not in any child's, so this
230+
// context must be the innermost context.
218231
return this;
219232
}
220233

@@ -391,7 +404,24 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
391404
auto R = getSourceRange();
392405
if (R.isValid()) {
393406
OS << " src_range=";
394-
R.print(OS, SrcMgr, /*PrintText=*/false);
407+
408+
if (getReason() != Reason::Root) {
409+
R.print(OS, SrcMgr, /*PrintText=*/false);
410+
} else if (auto info = SrcMgr.getGeneratedSourceInfo(
411+
Node.getAsSourceFile()->getBufferID())) {
412+
info->originalSourceRange.print(OS, SrcMgr, /*PrintText=*/false);
413+
} else {
414+
OS << "<unknown>";
415+
}
416+
}
417+
418+
if (getReason() == Reason::Root) {
419+
if (auto info = SrcMgr.getGeneratedSourceInfo(
420+
Node.getAsSourceFile()->getBufferID())) {
421+
OS << " generated_kind=" << GeneratedSourceInfo::kindToString(info->kind);
422+
} else {
423+
OS << " file=" << Node.getAsSourceFile()->getFilename().str();
424+
}
395425
}
396426

397427
if (!ExplicitAvailabilityInfo.isAlwaysAvailable())
@@ -462,3 +492,78 @@ void ExpandChildTypeRefinementContextsRequest::cacheResult(
462492
TRC->Children = children;
463493
TRC->setNeedsExpansion(false);
464494
}
495+
496+
/// Emit an error message, dump each context with its corresponding label, and
497+
/// abort.
498+
static void
499+
verificationError(ASTContext &ctx, llvm::StringRef msg,
500+
std::initializer_list<
501+
std::pair<const char *, const TypeRefinementContext *>>
502+
labelsAndNodes) {
503+
llvm::errs() << msg << "\n";
504+
for (auto pair : labelsAndNodes) {
505+
auto label = std::get<0>(pair);
506+
auto context = std::get<1>(pair);
507+
llvm::errs() << label << ":\n";
508+
context->print(llvm::errs(), ctx.SourceMgr);
509+
}
510+
abort();
511+
}
512+
513+
void TypeRefinementContext::verify(const TypeRefinementContext *parent,
514+
ASTContext &ctx) const {
515+
// Verify the children first.
516+
for (auto child : Children) {
517+
child->verify(this, ctx);
518+
}
519+
520+
// Verify that the children are in sorted order and that their source ranges
521+
// do not overlap.
522+
auto &srcMgr = ctx.SourceMgr;
523+
if (Children.size() > 1) {
524+
auto const *previous = Children.front();
525+
for (auto const *current : ArrayRef(Children).drop_front()) {
526+
if (!srcMgr.isAtOrBefore(previous->getSourceRange().Start,
527+
current->getSourceRange().Start))
528+
verificationError(
529+
ctx, "out of order children",
530+
{{"child 1", previous}, {"child 2", current}, {"parent", this}});
531+
532+
if (srcMgr.containsLoc(previous->getSourceRange(),
533+
current->getSourceRange().Start))
534+
verificationError(
535+
ctx, "overlapping children",
536+
{{"child 1", previous}, {"child 2", current}, {"parent", this}});
537+
538+
previous = current;
539+
}
540+
}
541+
542+
// Only root nodes are allowed to have no parent.
543+
if (!parent) {
544+
if (getReason() != Reason::Root)
545+
verificationError(ctx, "interior node without parent", {{"node", this}});
546+
return;
547+
}
548+
549+
// All nodes with a parent must have a valid source range.
550+
if (!SrcRange.isValid())
551+
verificationError(ctx, "invalid source range", {{"node", this}});
552+
553+
if (getReason() != Reason::Root) {
554+
auto parentRange = parent->SrcRange;
555+
if (parentRange.isValid() &&
556+
!(srcMgr.isAtOrBefore(parentRange.Start, SrcRange.Start) &&
557+
srcMgr.isAtOrBefore(SrcRange.End, parentRange.End)))
558+
verificationError(ctx, "child source range not contained",
559+
{{"child", this}, {"parent", this}});
560+
}
561+
562+
if (!AvailabilityInfo.isContainedIn(parent->AvailabilityInfo))
563+
verificationError(ctx, "child availability range not contained",
564+
{{"child", this}, {"parent", this}});
565+
}
566+
567+
void TypeRefinementContext::verify(ASTContext &ctx) {
568+
verify(nullptr, ctx);
569+
}

0 commit comments

Comments
 (0)