Skip to content

Commit eeeb27d

Browse files
committed
[cxx-interop] Add members to the LookupTable where possible.
If possible, add imported members to the StructDecl's LookupTable rather than adding them directly as members. This will fix the issues with ordering that #39436 poorly attempted to solve during IRGen. This also allows us to break out most of the test changes from #39436.
1 parent e845af8 commit eeeb27d

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
@@ -3073,6 +3073,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
30733073
/// Prepare to traverse the list of extensions.
30743074
void prepareExtensions();
30753075

3076+
/// Add loaded members from all extensions. Eagerly load any members that we
3077+
/// can't lazily load.
3078+
void addLoadedExtensions();
3079+
30763080
/// Retrieve the conformance loader (if any), and removing it in the
30773081
/// same operation. The caller is responsible for loading the
30783082
/// conformances.
@@ -3195,6 +3199,11 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
31953199
/// Add a new extension to this nominal type.
31963200
void addExtension(ExtensionDecl *extension);
31973201

3202+
/// Add a member to this decl's lookup table.
3203+
///
3204+
/// Calls "prepareLookupTable" as a side effect.
3205+
void addMemberToLookupTable(Decl *member);
3206+
31983207
/// Retrieve the set of extensions of this type.
31993208
ExtensionRange getExtensions();
32003209

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
@@ -4053,7 +4053,8 @@ ClangImporter::Implementation::loadNamedMembers(
40534053
auto table = findLookupTable(*CMO);
40544054
assert(table && "clang module without lookup table");
40554055

4056-
assert(isa<clang::ObjCContainerDecl>(CD) || isa<clang::NamespaceDecl>(CD));
4056+
assert(isa<clang::ObjCContainerDecl>(CD) || isa<clang::NamespaceDecl>(CD) ||
4057+
isa<clang::RecordDecl>(CD));
40574058

40584059
// Force the members of the entire inheritance hierarchy to be loaded and
40594060
// 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();
@@ -9875,6 +9826,38 @@ static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) {
98759826
E->loadAllMembers();
98769827
}
98779828

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

@@ -9893,26 +9876,8 @@ ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) {
98939876
return;
98949877
}
98959878

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

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)