Skip to content

Commit 40422f0

Browse files
authored
Merge pull request swiftlang#39664 from zoecarver/lazy-pt9-use-lookup-table
[cxx-interop] Add members to the LookupTable where possible.
2 parents 829f5b1 + eeeb27d commit 40422f0

30 files changed

+839
-792
lines changed

include/swift/AST/Decl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,6 +3078,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
30783078
/// Prepare to traverse the list of extensions.
30793079
void prepareExtensions();
30803080

3081+
/// Add loaded members from all extensions. Eagerly load any members that we
3082+
/// can't lazily load.
3083+
void addLoadedExtensions();
3084+
30813085
/// Retrieve the conformance loader (if any), and removing it in the
30823086
/// same operation. The caller is responsible for loading the
30833087
/// conformances.
@@ -3200,6 +3204,11 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
32003204
/// Add a new extension to this nominal type.
32013205
void addExtension(ExtensionDecl *extension);
32023206

3207+
/// Add a member to this decl's lookup table.
3208+
///
3209+
/// Calls "prepareLookupTable" as a side effect.
3210+
void addMemberToLookupTable(Decl *member);
3211+
32033212
/// Retrieve the set of extensions of this type.
32043213
ExtensionRange getExtensions();
32053214

lib/AST/DeclContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint,
867867
if (d->isImplicit())
868868
return true;
869869

870+
// Imported decls won't have complete location info.
871+
if (d->hasClangNode())
872+
return true;
873+
870874
return false;
871875
};
872876

lib/AST/NameLookup.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,12 @@ void ExtensionDecl::addedMember(Decl *member) {
12201220
}
12211221
}
12221222

1223+
void NominalTypeDecl::addMemberToLookupTable(Decl *member) {
1224+
prepareLookupTable();
1225+
1226+
LookupTable->addMember(member);
1227+
}
1228+
12231229
// For lack of anywhere more sensible to put it, here's a diagram of the pieces
12241230
// involved in finding members and extensions of a NominalTypeDecl.
12251231
//
@@ -1332,7 +1338,9 @@ void NominalTypeDecl::prepareLookupTable() {
13321338
} else {
13331339
LookupTable->addMembers(getMembers());
13341340
}
1341+
}
13351342

1343+
void NominalTypeDecl::addLoadedExtensions() {
13361344
for (auto e : getExtensions()) {
13371345
// If we can lazy-load this extension, only take the members we've loaded
13381346
// so far.
@@ -1424,10 +1432,10 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
14241432

14251433
decl->prepareLookupTable();
14261434

1427-
// If we're allowed to load extensions, call prepareExtensions to ensure we
1435+
// If we're allowed to load extensions, call addLoadedExtensions to ensure we
14281436
// properly invalidate the lazily-complete cache for any extensions brought in
14291437
// by modules loaded after-the-fact. This can happen with the LLDB REPL.
1430-
decl->prepareExtensions();
1438+
decl->addLoadedExtensions();
14311439

14321440
auto &Table = *decl->LookupTable;
14331441
if (!useNamedLazyMemberLoading) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4051,7 +4051,8 @@ ClangImporter::Implementation::loadNamedMembers(
40514051
auto table = findLookupTable(*CMO);
40524052
assert(table && "clang module without lookup table");
40534053

4054-
assert(isa<clang::ObjCContainerDecl>(CD) || isa<clang::NamespaceDecl>(CD));
4054+
assert(isa<clang::ObjCContainerDecl>(CD) || isa<clang::NamespaceDecl>(CD) ||
4055+
isa<clang::RecordDecl>(CD));
40554056

40564057
// Force the members of the entire inheritance hierarchy to be loaded and
40574058
// deserialized before loading the named member of a class. This warms up

lib/ClangImporter/ImportDecl.cpp

Lines changed: 45 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,48 +2401,6 @@ namespace {
24012401
if (auto *typedefForAnon = decl->getTypedefNameForAnonDecl())
24022402
return importFullName(typedefForAnon);
24032403
}
2404-
2405-
if (!decl->isRecord())
2406-
return {ImportedName(), None};
2407-
2408-
// If the type has no name and no structure name, but is not anonymous,
2409-
// generate a name for it. Specifically this is for cases like:
2410-
// struct a {
2411-
// struct {} z;
2412-
// }
2413-
// Where the member z is an unnamed struct, but does have a member-name
2414-
// and is accessible as a member of struct a.
2415-
if (auto recordDecl = dyn_cast<clang::RecordDecl>(
2416-
decl->getLexicalDeclContext())) {
2417-
for (auto field : recordDecl->fields()) {
2418-
if (field->getType()->getAsTagDecl() == decl) {
2419-
// Create a name for the declaration from the field name.
2420-
std::string Id;
2421-
llvm::raw_string_ostream IdStream(Id);
2422-
2423-
const char *kind;
2424-
if (decl->isStruct())
2425-
kind = "struct";
2426-
else if (decl->isUnion())
2427-
kind = "union";
2428-
else
2429-
llvm_unreachable("unknown decl kind");
2430-
2431-
IdStream << "__Unnamed_" << kind << "_";
2432-
if (field->isAnonymousStructOrUnion()) {
2433-
IdStream << "__Anonymous_field" << field->getFieldIndex();
2434-
} else {
2435-
assert(!field->getDeclName().isEmpty() &&
2436-
"Microsoft anonymous struct extension?");
2437-
IdStream << field->getName();
2438-
}
2439-
ImportedName Result;
2440-
Result.setDeclName(Impl.SwiftContext.getIdentifier(IdStream.str()));
2441-
Result.setEffectiveContext(decl->getDeclContext());
2442-
return {Result, None};
2443-
}
2444-
}
2445-
}
24462404

24472405
return {ImportedName(), None};
24482406
}
@@ -3634,7 +3592,7 @@ namespace {
36343592
if (auto nominalDecl =
36353593
dyn_cast<NominalTypeDecl>(nestedType->getDeclContext())) {
36363594
assert(nominalDecl == result && "interesting nesting of C types?");
3637-
nominalDecl->addMember(nestedType);
3595+
nominalDecl->addMemberToLookupTable(nestedType);
36383596
}
36393597
}
36403598

@@ -3675,7 +3633,7 @@ namespace {
36753633
/*wantBody=*/true);
36763634
ctors.push_back(valueCtor);
36773635
}
3678-
result->addMember(member);
3636+
result->addMemberToLookupTable(member);
36793637
}
36803638

36813639
const clang::CXXRecordDecl *cxxRecordDecl =
@@ -3710,12 +3668,14 @@ namespace {
37103668
ctors.push_back(valueCtor);
37113669
}
37123670

3671+
// Add ctors directly as they cannot always be looked up from the clang
3672+
// decl (some are synthesized by Swift).
37133673
for (auto ctor : ctors) {
37143674
result->addMember(ctor);
37153675
}
37163676

37173677
for (auto method : methods) {
3718-
result->addMember(method);
3678+
result->addMemberToLookupTable(method);
37193679
}
37203680

37213681
result->setHasUnreferenceableStorage(hasUnreferenceableStorage);
@@ -3731,10 +3691,13 @@ namespace {
37313691
auto getterAndSetter = subscriptInfo.second;
37323692
auto subscript = makeSubscript(getterAndSetter.first,
37333693
getterAndSetter.second);
3694+
// Also add subscripts directly because they won't be found from the
3695+
// clang decl.
37343696
result->addMember(subscript);
37353697
}
37363698
}
37373699

3700+
result->setMemberLoader(&Impl, 0);
37383701
return result;
37393702
}
37403703

@@ -4363,21 +4326,9 @@ namespace {
43634326
Optional<ImportedName> correctSwiftName;
43644327
ImportedName importedName;
43654328

4366-
if (!decl->isAnonymousStructOrUnion() && !decl->getDeclName().isEmpty()) {
4367-
std::tie(importedName, correctSwiftName) = importFullName(decl);
4368-
if (!importedName) {
4369-
return nullptr;
4370-
}
4371-
} else {
4372-
// Generate a field name for anonymous fields, this will be used in
4373-
// order to be able to expose the indirect fields injected from there
4374-
// as computed properties forwarding the access to the subfield.
4375-
std::string Id;
4376-
llvm::raw_string_ostream IdStream(Id);
4377-
4378-
IdStream << "__Anonymous_field" << decl->getFieldIndex();
4379-
importedName.setDeclName(Impl.SwiftContext.getIdentifier(IdStream.str()));
4380-
importedName.setEffectiveContext(decl->getDeclContext());
4329+
std::tie(importedName, correctSwiftName) = importFullName(decl);
4330+
if (!importedName) {
4331+
return nullptr;
43814332
}
43824333

43834334
auto name = importedName.getDeclName().getBaseIdentifier();
@@ -9874,6 +9825,38 @@ static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) {
98749825
E->loadAllMembers();
98759826
}
98769827

9828+
static void loadAllMembersOfRecordDecl(StructDecl *recordDecl) {
9829+
auto &ctx = recordDecl->getASTContext();
9830+
auto clangRecord = cast<clang::RecordDecl>(recordDecl->getClangDecl());
9831+
9832+
// This is only to keep track of the members we've already seen.
9833+
llvm::SmallPtrSet<Decl *, 16> addedMembers;
9834+
for (auto member : recordDecl->getCurrentMembersWithoutLoading())
9835+
addedMembers.insert(member);
9836+
9837+
for (auto member : clangRecord->decls()) {
9838+
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
9839+
if (!namedDecl)
9840+
continue;
9841+
9842+
// Don't try to load ourselves (this will create an infinite loop).
9843+
if (auto possibleSelf = dyn_cast<clang::RecordDecl>(member))
9844+
if (possibleSelf->getDefinition() == clangRecord)
9845+
continue;
9846+
9847+
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
9848+
if (!name)
9849+
continue;
9850+
9851+
for (auto found : recordDecl->lookupDirect(name)) {
9852+
if (addedMembers.insert(found).second &&
9853+
// TODO: remove this check once PR#38675 lands.
9854+
found->getDeclContext() == recordDecl)
9855+
recordDecl->addMember(found);
9856+
}
9857+
}
9858+
}
9859+
98779860
void
98789861
ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) {
98799862

@@ -9892,26 +9875,8 @@ ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) {
98929875
return;
98939876
}
98949877

9895-
auto namespaceDecl =
9896-
dyn_cast_or_null<clang::NamespaceDecl>(D->getClangDecl());
9897-
if (namespaceDecl) {
9898-
auto *enumDecl = cast<EnumDecl>(D);
9899-
// TODO: This redecls should only match redecls that are in the same
9900-
// module as namespaceDecl after we import one namespace per clang module.
9901-
for (auto ns : namespaceDecl->redecls()) {
9902-
for (auto m : ns->decls()) {
9903-
auto nd = dyn_cast<clang::NamedDecl>(m);
9904-
if (!nd)
9905-
continue;
9906-
auto member = importDecl(nd, CurrentVersion);
9907-
if (!member)
9908-
continue;
9909-
9910-
// TODO: remove this change when #34706 lands.
9911-
if (!member->NextDecl)
9912-
enumDecl->addMember(member);
9913-
}
9914-
}
9878+
if (D->getClangDecl() && isa<clang::RecordDecl>(D->getClangDecl())) {
9879+
loadAllMembersOfRecordDecl(cast<StructDecl>(D));
99159880
return;
99169881
}
99179882

lib/ClangImporter/ImportName.cpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,9 +1670,67 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
16701670
}
16711671
}
16721672

1673-
// For empty names, there is nothing to do.
1674-
if (D->getDeclName().isEmpty())
1673+
// Spcial case: unnamed/anonymous fields.
1674+
if (auto field = dyn_cast<clang::FieldDecl>(D)) {
1675+
if (field->isAnonymousStructOrUnion() || field->getDeclName().isEmpty()) {
1676+
// Generate a field name for anonymous fields, this will be used in
1677+
// order to be able to expose the indirect fields injected from there
1678+
// as computed properties forwarding the access to the subfield.
1679+
std::string name;
1680+
llvm::raw_string_ostream nameStream(name);
1681+
1682+
nameStream << "__Anonymous_field" << field->getFieldIndex();
1683+
result.setDeclName(swiftCtx.getIdentifier(nameStream.str()));
1684+
result.setEffectiveContext(field->getDeclContext());
1685+
return result;
1686+
}
1687+
}
1688+
1689+
if (D->getDeclName().isEmpty()) {
1690+
// If the type has no name and no structure name, but is not anonymous,
1691+
// generate a name for it. Specifically this is for cases like:
1692+
// struct a {
1693+
// struct {} z;
1694+
// }
1695+
// Where the member z is an unnamed struct, but does have a member-name
1696+
// and is accessible as a member of struct a.
1697+
if (auto recordDecl = dyn_cast<clang::RecordDecl>(
1698+
D->getLexicalDeclContext())) {
1699+
for (auto field : recordDecl->fields()) {
1700+
auto fieldTagDecl = field->getType()->getAsTagDecl();
1701+
if (fieldTagDecl == D) {
1702+
// Create a name for the declaration from the field name.
1703+
std::string name;
1704+
llvm::raw_string_ostream nameStream(name);
1705+
1706+
const char *kind;
1707+
if (fieldTagDecl->isStruct())
1708+
kind = "struct";
1709+
else if (fieldTagDecl->isUnion())
1710+
kind = "union";
1711+
else if (fieldTagDecl->isEnum())
1712+
kind = "enum";
1713+
else
1714+
llvm_unreachable("unknown decl kind");
1715+
1716+
nameStream << "__Unnamed_" << kind << "_";
1717+
if (field->isAnonymousStructOrUnion()) {
1718+
nameStream << "__Anonymous_field" << field->getFieldIndex();
1719+
} else {
1720+
assert(!field->getDeclName().isEmpty() &&
1721+
"Microsoft anonymous struct extension?");
1722+
nameStream << field->getName();
1723+
}
1724+
result.setDeclName(swiftCtx.getIdentifier(nameStream.str()));
1725+
result.setEffectiveContext(D->getDeclContext());
1726+
return result;
1727+
}
1728+
}
1729+
}
1730+
1731+
// Otherwise, for empty names, there is nothing to do.
16751732
return result;
1733+
}
16761734

16771735
/// Whether the result is a function name.
16781736
bool isFunction = false;

test/ClangImporter/Inputs/SwiftPrivateAttr.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@ class __PrivFooSub : Foo {
4141
}
4242
func __privTest()
4343
struct S0 {
44-
var __privValue: Int32
4544
init()
4645
init(__privValue: Int32)
46+
var __privValue: Int32
4747
}
4848
struct __PrivS1 {
49-
var value: Int32
5049
init()
5150
init(value: Int32)
51+
var value: Int32
5252
}
5353
struct __PrivS2 {
54-
var value: Int32
5554
init()
5655
init(value: Int32)
56+
var value: Int32
5757
}
5858
var __PrivAnonymousA: Int { get }
5959
struct E0 : Equatable, RawRepresentable {

test/ClangImporter/ctypes_parse.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift %clang-importer-sdk
1+
// RUN: %target-typecheck-verify-swift %clang-importer-sdk -verify-ignore-unknown
22

33
import ctypes
44

test/IDE/newtype.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@
128128
// PRINT-NEXT: func takeMyABIOldTypeNonNullNS(_: String)
129129
//
130130
// PRINT-NEXT: struct NSSomeContext {
131-
// PRINT-NEXT: var i: Int32
132131
// PRINT-NEXT: init()
133132
// PRINT-NEXT: init(i: Int32)
133+
// PRINT-NEXT: var i: Int32
134134
// PRINT-NEXT: }
135135
// PRINT-NEXT: extension NSSomeContext {
136136
// PRINT-NEXT: struct Name : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {

0 commit comments

Comments
 (0)