Skip to content

Commit 8bcfafe

Browse files
authored
Merge pull request #13216 from graydon/force-on-named-lazy-member-loading-retry
Force on named lazy member loading retry
2 parents 6cf9ed8 + c51bb7c commit 8bcfafe

25 files changed

+321
-153
lines changed

include/swift/AST/DeclContext.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,10 +610,15 @@ class IterableDeclContext {
610610
mutable llvm::PointerIntPair<Decl *, 2, IterableDeclContextKind>
611611
LastDeclAndKind;
612612

613-
// The DeclID this IDC was deserialized from, if any. Used for named lazy
614-
// member loading, as a key when doing lookup in this IDC.
613+
/// The DeclID this IDC was deserialized from, if any. Used for named lazy
614+
/// member loading, as a key when doing lookup in this IDC.
615615
serialization::DeclID SerialID;
616616

617+
/// Lazy member loading has a variety of feedback loops that need to
618+
/// switch to pseudo-empty-member behaviour to avoid infinite recursion;
619+
/// we use this flag to control them.
620+
bool lazyMemberLoadingInProgress = false;
621+
617622
template<class A, class B, class C>
618623
friend struct ::llvm::cast_convert_val;
619624

@@ -634,6 +639,11 @@ class IterableDeclContext {
634639
/// Retrieve the set of members in this context.
635640
DeclRange getMembers() const;
636641

642+
/// Retrieve the set of members in this context without loading any from the
643+
/// associated lazy loader; this should only be used as part of implementing
644+
/// abstractions on top of member loading, such as a name lookup table.
645+
DeclRange getCurrentMembersWithoutLoading() const;
646+
637647
/// Add a member to this context. If the hint decl is specified, the new decl
638648
/// is inserted immediately after the hint.
639649
void addMember(Decl *member, Decl *hint = nullptr);
@@ -643,6 +653,14 @@ class IterableDeclContext {
643653
return FirstDeclAndLazyMembers.getInt();
644654
}
645655

656+
bool isLoadingLazyMembers() {
657+
return lazyMemberLoadingInProgress;
658+
}
659+
660+
void setLoadingLazyMembers(bool inProgress) {
661+
lazyMemberLoadingInProgress = inProgress;
662+
}
663+
646664
/// Setup the loader for lazily-loaded members.
647665
void setMemberLoader(LazyMemberLoader *loader, uint64_t contextData);
648666

include/swift/Basic/LangOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ namespace swift {
159159
bool IterativeTypeChecker = false;
160160

161161
/// \brief Enable named lazy member loading.
162-
bool NamedLazyMemberLoading = false;
162+
bool NamedLazyMemberLoading = true;
163163

164164
/// Debug the generic signatures computed by the generic signature builder.
165165
bool DebugGenericSignatures = false;

include/swift/Option/FrontendOptions.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ def propagate_constraints : Flag<["-"], "propagate-constraints">,
155155
def iterative_type_checker : Flag<["-"], "iterative-type-checker">,
156156
HelpText<"Enable the iterative type checker">;
157157

158-
def enable_named_lazy_member_loading : Flag<["-"], "enable-named-lazy-member-loading">,
159-
HelpText<"Enable per-name lazy member loading">;
158+
def disable_named_lazy_member_loading : Flag<["-"], "disable-named-lazy-member-loading">,
159+
HelpText<"Disable per-name lazy member loading">;
160160

161161
def debug_generic_signatures : Flag<["-"], "debug-generic-signatures">,
162162
HelpText<"Debug generic signatures">;

lib/AST/DeclContext.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,10 +872,14 @@ ASTContext &IterableDeclContext::getASTContext() const {
872872
return getDecl()->getASTContext();
873873
}
874874

875+
DeclRange IterableDeclContext::getCurrentMembersWithoutLoading() const {
876+
return DeclRange(FirstDeclAndLazyMembers.getPointer(), nullptr);
877+
}
878+
875879
DeclRange IterableDeclContext::getMembers() const {
876880
loadAllMembers();
877881

878-
return DeclRange(FirstDeclAndLazyMembers.getPointer(), nullptr);
882+
return getCurrentMembersWithoutLoading();
879883
}
880884

881885
/// Add a member to this context.

lib/AST/NameLookup.cpp

Lines changed: 111 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,15 +1024,6 @@ class swift::MemberLookupTable {
10241024
return Lookup.find(name);
10251025
}
10261026

1027-
/// \brief Add an empty entry to the lookup map for a given name if it does
1028-
/// not yet have one.
1029-
void addEmptyEntry(DeclName name) {
1030-
(void)Lookup[name];
1031-
if (!name.isSimpleName()) {
1032-
(void)Lookup[name.getBaseName()];
1033-
}
1034-
}
1035-
10361027
// \brief Mark all Decls in this table as not-resident in a table, drop
10371028
// references to them. Should only be called when this was not fully-populated
10381029
// from an IterableDeclContext.
@@ -1247,55 +1238,19 @@ void ExtensionDecl::addedMember(Decl *member) {
12471238
// In all lookup routines, the 'ignoreNewExtensions' flag means that
12481239
// lookup should only use the set of extensions already observed.
12491240

1250-
void NominalTypeDecl::prepareLookupTable(bool ignoreNewExtensions) {
1251-
// If we haven't allocated the lookup table yet, do so now.
1252-
if (!LookupTable.getPointer()) {
1253-
auto &ctx = getASTContext();
1254-
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
1255-
}
1256-
1257-
// If we're an IDC that has not yet loaded its full member set, we're doing
1258-
// NamedLazyMemberLoading, and we should not force getMembers().
1259-
if (hasLazyMembers())
1260-
return;
1261-
1262-
// If we haven't walked the member list yet to update the lookup
1263-
// table, do so now.
1264-
if (!LookupTable.getInt()) {
1265-
// Note that we'll have walked the members now.
1266-
LookupTable.setInt(true);
1267-
1268-
// Add the members of the nominal declaration to the table.
1269-
LookupTable.getPointer()->addMembers(getMembers());
1270-
}
1271-
1272-
if (!ignoreNewExtensions) {
1273-
// Update the lookup table to introduce members from extensions.
1274-
LookupTable.getPointer()->updateLookupTable(this);
1275-
}
1276-
}
1277-
1278-
void NominalTypeDecl::makeMemberVisible(ValueDecl *member) {
1279-
if (!LookupTable.getPointer()) {
1280-
auto &ctx = getASTContext();
1281-
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
1282-
}
1283-
1284-
LookupTable.getPointer()->addMember(member);
1285-
}
1286-
12871241
static bool
12881242
populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx,
12891243
MemberLookupTable &LookupTable,
12901244
DeclName name,
12911245
IterableDeclContext *IDC) {
1246+
if (IDC->isLoadingLazyMembers()) {
1247+
return false;
1248+
}
1249+
IDC->setLoadingLazyMembers(true);
12921250
auto ci = ctx.getOrCreateLazyIterableContextData(IDC,
12931251
/*lazyLoader=*/nullptr);
1294-
// Populate LookupTable with an empty vector before we call into our loader,
1295-
// so that any reentry of this routine will find the set-so-far, and not
1296-
// fall into infinite recursion.
1297-
LookupTable.addEmptyEntry(name);
12981252
if (auto res = ci->loader->loadNamedMembers(IDC, name, ci->memberData)) {
1253+
IDC->setLoadingLazyMembers(false);
12991254
if (auto s = ctx.Stats) {
13001255
++s->getFrontendCounters().NamedLazyMemberLoadSuccessCount;
13011256
}
@@ -1304,19 +1259,18 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx,
13041259
}
13051260
return false;
13061261
} else {
1262+
IDC->setLoadingLazyMembers(false);
13071263
if (auto s = ctx.Stats) {
13081264
++s->getFrontendCounters().NamedLazyMemberLoadFailureCount;
13091265
}
13101266
return true;
13111267
}
13121268
}
13131269

1314-
static void
1315-
populateLookupTableEntryFromMembers(ASTContext &ctx,
1316-
MemberLookupTable &LookupTable,
1317-
DeclName name,
1318-
IterableDeclContext *IDC) {
1319-
for (auto m : IDC->getMembers()) {
1270+
static void populateLookupTableEntryFromCurrentMembersWithoutLoading(
1271+
ASTContext &ctx, MemberLookupTable &LookupTable, DeclName name,
1272+
IterableDeclContext *IDC) {
1273+
for (auto m : IDC->getCurrentMembersWithoutLoading()) {
13201274
if (auto v = dyn_cast<ValueDecl>(m)) {
13211275
if (v->getFullName().matchesRef(name)) {
13221276
LookupTable.addMember(m);
@@ -1325,6 +1279,75 @@ populateLookupTableEntryFromMembers(ASTContext &ctx,
13251279
}
13261280
}
13271281

1282+
static bool
1283+
populateLookupTableEntryFromExtensions(ASTContext &ctx,
1284+
MemberLookupTable &table,
1285+
NominalTypeDecl *nominal,
1286+
DeclName name,
1287+
bool ignoreNewExtensions) {
1288+
if (!ignoreNewExtensions) {
1289+
for (auto e : nominal->getExtensions()) {
1290+
if (e->wasDeserialized() || e->hasClangNode()) {
1291+
if (populateLookupTableEntryFromLazyIDCLoader(ctx, table,
1292+
name, e)) {
1293+
return true;
1294+
}
1295+
} else {
1296+
populateLookupTableEntryFromCurrentMembersWithoutLoading(ctx, table,
1297+
name, e);
1298+
}
1299+
}
1300+
}
1301+
return false;
1302+
}
1303+
1304+
void NominalTypeDecl::prepareLookupTable(bool ignoreNewExtensions) {
1305+
// If we haven't allocated the lookup table yet, do so now.
1306+
if (!LookupTable.getPointer()) {
1307+
auto &ctx = getASTContext();
1308+
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
1309+
}
1310+
1311+
if (hasLazyMembers()) {
1312+
// Lazy members: if the table needs population, populate the table _only
1313+
// from those members already in the IDC member list_ such as implicits or
1314+
// globals-as-members, then update table entries from the extensions that
1315+
// have the same names as any such initial-population members.
1316+
if (!LookupTable.getInt()) {
1317+
LookupTable.setInt(true);
1318+
LookupTable.getPointer()->addMembers(getCurrentMembersWithoutLoading());
1319+
for (auto *m : getCurrentMembersWithoutLoading()) {
1320+
if (auto v = dyn_cast<ValueDecl>(m)) {
1321+
populateLookupTableEntryFromExtensions(getASTContext(),
1322+
*LookupTable.getPointer(),
1323+
this, v->getBaseName(),
1324+
ignoreNewExtensions);
1325+
}
1326+
}
1327+
}
1328+
1329+
} else {
1330+
// No lazy members: if the table needs population, populate the table
1331+
// en-masse; and in either case update the extensions.
1332+
if (!LookupTable.getInt()) {
1333+
LookupTable.setInt(true);
1334+
LookupTable.getPointer()->addMembers(getMembers());
1335+
}
1336+
if (!ignoreNewExtensions) {
1337+
LookupTable.getPointer()->updateLookupTable(this);
1338+
}
1339+
}
1340+
}
1341+
1342+
void NominalTypeDecl::makeMemberVisible(ValueDecl *member) {
1343+
if (!LookupTable.getPointer()) {
1344+
auto &ctx = getASTContext();
1345+
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
1346+
}
1347+
1348+
LookupTable.getPointer()->addMember(member);
1349+
}
1350+
13281351
TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
13291352
DeclName name,
13301353
bool ignoreNewExtensions) {
@@ -1336,16 +1359,24 @@ TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
13361359
.NominalTypeDecl__lookupDirect.getGuard();
13371360
}
13381361

1339-
DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" << name << ")"
1340-
<< ", lookupTable.getInt()=" << LookupTable.getInt()
1341-
<< ", hasLazyMembers()=" << hasLazyMembers()
1342-
<< "\n");
1343-
13441362
// We only use NamedLazyMemberLoading when a user opts-in and we have
13451363
// not yet loaded all the members into the IDC list in the first place.
13461364
bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading &&
13471365
hasLazyMembers());
13481366

1367+
// FIXME: At present, lazy member loading conflicts with a bunch of other code
1368+
// that appears to special-case initializers (clang-imported initializer
1369+
// sorting, implicit initializer synthesis), so for the time being we have to
1370+
// turn it off for them entirely.
1371+
if (name.getBaseName() == ctx.Id_init)
1372+
useNamedLazyMemberLoading = false;
1373+
1374+
DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" << name << ")"
1375+
<< ", lookupTable.getInt()=" << LookupTable.getInt()
1376+
<< ", hasLazyMembers()=" << hasLazyMembers()
1377+
<< ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading
1378+
<< "\n");
1379+
13491380
// We check the LookupTable at most twice, possibly treating a miss in the
13501381
// first try as a cache-miss that we then do a cache-fill on, and retry.
13511382
for (int i = 0; i < 2; ++i) {
@@ -1354,12 +1385,22 @@ TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
13541385
// populated the IDC and brought it up to date with any extensions. This
13551386
// will flip the hasLazyMembers() flag to false as well.
13561387
if (!useNamedLazyMemberLoading) {
1357-
// If we're about to load members here, purge the MemberLookupTable;
1358-
// it will be rebuilt in prepareLookup, below.
1359-
if (hasLazyMembers() && LookupTable.getPointer()) {
1360-
// We should not have scanned the IDC list yet. Double check.
1361-
assert(!LookupTable.getInt());
1388+
// It's possible that the lookup table exists but has information in it
1389+
// that is either currently out of date or soon to be out of date.
1390+
// This can happen two ways:
1391+
//
1392+
// - We've not yet indexed the members we have (LookupTable.getInt()
1393+
// is zero).
1394+
//
1395+
// - We've still got more lazy members left to load; this can happen
1396+
// even if we _did_ index some members.
1397+
//
1398+
// In either of these cases, we want to reset the table to empty and
1399+
// mark it as needing reconstruction.
1400+
if (LookupTable.getPointer() &&
1401+
(hasLazyMembers() || !LookupTable.getInt())) {
13621402
LookupTable.getPointer()->clear();
1403+
LookupTable.setInt(false);
13631404
}
13641405

13651406
(void)getMembers();
@@ -1373,8 +1414,7 @@ TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
13731414
}
13741415

13751416
// Next, in all cases, prepare the lookup table for use, possibly
1376-
// repopulating it from the IDC if just did our initial IDC population
1377-
// above.
1417+
// repopulating it from the IDC if the IDC member list has just grown.
13781418
prepareLookupTable(ignoreNewExtensions);
13791419

13801420
// Look for a declaration with this name.
@@ -1395,23 +1435,10 @@ TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
13951435
// false, and we fall back to loading all members during the retry.
13961436
auto &Table = *LookupTable.getPointer();
13971437
if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table,
1398-
name, this)) {
1438+
name, this) ||
1439+
populateLookupTableEntryFromExtensions(ctx, Table, this, name,
1440+
ignoreNewExtensions)) {
13991441
useNamedLazyMemberLoading = false;
1400-
} else {
1401-
if (!ignoreNewExtensions) {
1402-
for (auto E : getExtensions()) {
1403-
if (E->wasDeserialized()) {
1404-
if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table,
1405-
name, E)) {
1406-
useNamedLazyMemberLoading = false;
1407-
break;
1408-
}
1409-
} else {
1410-
populateLookupTableEntryFromMembers(ctx, Table,
1411-
name, E);
1412-
}
1413-
}
1414-
}
14151442
}
14161443
}
14171444

0 commit comments

Comments
 (0)