Skip to content

Commit cdc3e6c

Browse files
authored
Merge pull request #11013 from jrose-apple/4.0-lookupNestedType-for-Clang
[4.0] [ClangImporter] Add direct access for import-as-member types. SR-5284 / rdar://problem/32926560
2 parents c3ff4e3 + c48ce47 commit cdc3e6c

File tree

21 files changed

+294
-75
lines changed

21 files changed

+294
-75
lines changed

include/swift/AST/Module.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,18 @@ class FileUnit : public DeclContext {
558558
return nullptr;
559559
}
560560

561+
/// Directly look for a nested type declared within this module inside the
562+
/// given nominal type (including any extensions).
563+
///
564+
/// This is a fast-path hack to avoid circular dependencies in deserialization
565+
/// and the Clang importer.
566+
///
567+
/// Private and fileprivate types should not be returned by this lookup.
568+
virtual TypeDecl *lookupNestedType(Identifier name,
569+
const NominalTypeDecl *parent) const {
570+
return nullptr;
571+
}
572+
561573
/// Find ValueDecls in the module and pass them to the given consumer object.
562574
///
563575
/// This does a simple local lookup, not recursively looking through imports.

include/swift/ClangImporter/ClangModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class ClangModuleUnit final : public LoadedFile {
6262
DeclName name, NLKind lookupKind,
6363
SmallVectorImpl<ValueDecl*> &results) const override;
6464

65+
virtual TypeDecl *
66+
lookupNestedType(Identifier name,
67+
const NominalTypeDecl *baseType) const override;
68+
6569
virtual void lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath,
6670
VisibleDeclConsumer &consumer,
6771
NLKind lookupKind) const override;

include/swift/Serialization/ModuleFile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ class ModuleFile : public LazyMemberLoader {
603603

604604
/// Searches the module's nested type decls table for the given member of
605605
/// the given type.
606-
TypeDecl *lookupNestedType(Identifier name, const ValueDecl *parent);
606+
TypeDecl *lookupNestedType(Identifier name, const NominalTypeDecl *parent);
607607

608608
/// Searches the module's operators for one with the given name and fixity.
609609
///

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ class SerializedASTFile final : public LoadedFile {
132132

133133
virtual TypeDecl *lookupLocalType(StringRef MangledName) const override;
134134

135+
virtual TypeDecl *
136+
lookupNestedType(Identifier name,
137+
const NominalTypeDecl *parent) const override;
138+
135139
virtual OperatorDecl *lookupOperator(Identifier name,
136140
DeclKind fixity) const override;
137141

lib/ClangImporter/ClangImporter.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,91 @@ static bool isVisibleClangEntry(clang::ASTContext &ctx,
24282428
return true;
24292429
}
24302430

2431+
TypeDecl *
2432+
ClangModuleUnit::lookupNestedType(Identifier name,
2433+
const NominalTypeDecl *baseType) const {
2434+
// Special case for error code enums: try looking directly into the struct
2435+
// first. But only if it looks like a synthesized error wrapped struct.
2436+
if (name == getASTContext().Id_Code && !baseType->hasClangNode() &&
2437+
isa<StructDecl>(baseType) && !baseType->hasLazyMembers() &&
2438+
baseType->isChildContextOf(this)) {
2439+
auto *mutableBase = const_cast<NominalTypeDecl *>(baseType);
2440+
auto codeEnum = mutableBase->lookupDirect(name,/*ignoreNewExtensions*/true);
2441+
// Double-check that we actually have a good result. It's possible what we
2442+
// found is /not/ a synthesized error struct, but just something that looks
2443+
// like it. But if we still found a good result we should return that.
2444+
if (codeEnum.size() == 1 && isa<TypeDecl>(codeEnum.front()))
2445+
return cast<TypeDecl>(codeEnum.front());
2446+
if (codeEnum.size() > 1)
2447+
return nullptr;
2448+
// Otherwise, fall back and try via lookup table.
2449+
}
2450+
2451+
auto lookupTable = owner.findLookupTable(clangModule);
2452+
if (!lookupTable)
2453+
return nullptr;
2454+
2455+
auto baseTypeContext = owner.getEffectiveClangContext(baseType);
2456+
if (!baseTypeContext)
2457+
return nullptr;
2458+
2459+
auto &clangCtx = owner.getClangASTContext();
2460+
2461+
// FIXME: This is very similar to what's in Implementation::lookupValue and
2462+
// Implementation::loadAllMembers.
2463+
SmallVector<TypeDecl *, 2> results;
2464+
for (auto entry : lookupTable->lookup(name.str(),
2465+
baseTypeContext)) {
2466+
// If the entry is not visible, skip it.
2467+
if (!isVisibleClangEntry(clangCtx, entry)) continue;
2468+
2469+
auto clangDecl = entry.dyn_cast<clang::NamedDecl *>();
2470+
auto clangTypeDecl = dyn_cast_or_null<clang::TypeDecl>(clangDecl);
2471+
if (!clangTypeDecl)
2472+
continue;
2473+
2474+
clangTypeDecl = cast<clang::TypeDecl>(clangTypeDecl->getMostRecentDecl());
2475+
2476+
bool anyMatching = false;
2477+
TypeDecl *originalDecl = nullptr;
2478+
owner.forEachDistinctName(clangTypeDecl, [&](ImportedName newName,
2479+
ImportNameVersion nameVersion){
2480+
if (anyMatching)
2481+
return;
2482+
if (!newName.getDeclName().isSimpleName(name))
2483+
return;
2484+
2485+
auto decl = dyn_cast_or_null<TypeDecl>(
2486+
owner.importDeclReal(clangTypeDecl, nameVersion));
2487+
if (!decl)
2488+
return;
2489+
2490+
if (!originalDecl)
2491+
originalDecl = decl;
2492+
else if (originalDecl == decl)
2493+
return;
2494+
2495+
auto *importedContext = decl->getDeclContext()->
2496+
getAsNominalTypeOrNominalTypeExtensionContext();
2497+
if (importedContext != baseType)
2498+
return;
2499+
2500+
assert(decl->getFullName().matchesRef(name) &&
2501+
"importFullName behaved differently from importDecl");
2502+
results.push_back(decl);
2503+
anyMatching = true;
2504+
});
2505+
}
2506+
2507+
if (results.size() != 1) {
2508+
// It's possible that two types were import-as-member'd onto the same base
2509+
// type with the same name. In this case, fall back to regular lookup.
2510+
return nullptr;
2511+
}
2512+
2513+
return results.front();
2514+
}
2515+
24312516
void ClangImporter::loadExtensions(NominalTypeDecl *nominal,
24322517
unsigned previousGeneration) {
24332518
// Determine the effective Clang context for this Swift nominal type.
@@ -3124,7 +3209,7 @@ void ClangImporter::Implementation::lookupAllObjCMembers(
31243209
}
31253210

31263211
EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
3127-
NominalTypeDecl *nominal) {
3212+
const NominalTypeDecl *nominal) {
31283213
// If we have a Clang declaration, look at it to determine the
31293214
// effective Clang context.
31303215
if (auto constClangDecl = nominal->getClangDecl()) {
@@ -3139,7 +3224,7 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
31393224

31403225
// Resolve the type.
31413226
if (auto typeResolver = getTypeResolver())
3142-
typeResolver->resolveDeclSignature(nominal);
3227+
typeResolver->resolveDeclSignature(const_cast<NominalTypeDecl *>(nominal));
31433228

31443229
// If it's an @objc entity, go look for it.
31453230
if (nominal->isObjC()) {

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11631163
VisibleDeclConsumer &consumer);
11641164

11651165
/// Determine the effective Clang context for the given Swift nominal type.
1166-
EffectiveClangContext getEffectiveClangContext(NominalTypeDecl *nominal);
1166+
EffectiveClangContext
1167+
getEffectiveClangContext(const NominalTypeDecl *nominal);
11671168

11681169
/// Attempts to import the name of \p decl with each possible
11691170
/// ImportNameVersion. \p action will be called with each unique name.

lib/Serialization/Deserialization.cpp

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
STATISTIC(NumDeclsLoaded, "# of decls deserialized");
3636
STATISTIC(NumMemberListsLoaded,
3737
"# of nominals/extensions whose members were loaded");
38+
STATISTIC(NumNormalProtocolConformancesLoaded,
39+
"# of normal protocol conformances deserialized");
40+
STATISTIC(NumNormalProtocolConformancesCompleted,
41+
"# of normal protocol conformances completed");
3842
STATISTIC(NumNestedTypeShortcuts,
3943
"# of same-module nested types resolved without lookup");
4044

@@ -108,18 +112,6 @@ namespace {
108112
XRefTracePath::print(os, "\t");
109113
}
110114
};
111-
112-
class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
113-
const char *Action;
114-
const ModuleFile *MF;
115-
public:
116-
explicit PrettyStackTraceModuleFile(const char *action, ModuleFile *module)
117-
: Action(action), MF(module) {}
118-
119-
void print(raw_ostream &os) const override {
120-
os << Action << " \'" << getNameOfModule(MF) << "'\n";
121-
}
122-
};
123115
} // end anonymous namespace
124116

125117
const char DeclDeserializationError::ID = '\0';
@@ -583,7 +575,7 @@ ProtocolConformanceRef ModuleFile::readConformance(
583575
nominal->lookupConformance(module, proto, conformances);
584576
PrettyStackTraceModuleFile traceMsg(
585577
"If you're seeing a crash here, check that your SDK and dependencies "
586-
"are at least as new as the versions used to build", this);
578+
"are at least as new as the versions used to build", *this);
587579
// This would normally be an assertion but it's more useful to print the
588580
// PrettyStackTrace here even in no-asserts builds.
589581
if (conformances.empty())
@@ -640,6 +632,7 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
640632

641633
auto proto = cast<ProtocolDecl>(getDecl(protoID));
642634
PrettyStackTraceDecl traceTo("... to", proto);
635+
++NumNormalProtocolConformancesLoaded;
643636

644637
auto conformance = ctx.getConformance(conformingType, proto, SourceLoc(), dc,
645638
ProtocolConformanceState::Incomplete);
@@ -1336,9 +1329,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13361329
&blobData);
13371330
switch (recordID) {
13381331
case XREF_TYPE_PATH_PIECE: {
1339-
if (values.size() == 1) {
1340-
ModuleDecl *module = values.front()->getModuleContext();
1341-
1332+
if (values.size() == 1 && isa<NominalTypeDecl>(values.front())) {
13421333
// Fast path for nested types that avoids deserializing all
13431334
// members of the parent type.
13441335
IdentifierID IID;
@@ -1351,29 +1342,27 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13511342
"If you're seeing a crash here, try passing "
13521343
"-Xfrontend -disable-serialization-nested-type-lookup-table"};
13531344

1345+
auto *baseType = cast<NominalTypeDecl>(values.front());
13541346
TypeDecl *nestedType = nullptr;
13551347
if (onlyInNominal) {
13561348
// Only look in the file containing the type itself.
13571349
const DeclContext *dc = values.front()->getDeclContext();
1358-
auto *serializedFile =
1359-
dyn_cast<SerializedASTFile>(dc->getModuleScopeContext());
1360-
if (serializedFile) {
1361-
nestedType =
1362-
serializedFile->File.lookupNestedType(memberName,
1363-
values.front());
1350+
auto *containingFile =
1351+
dyn_cast<FileUnit>(dc->getModuleScopeContext());
1352+
if (containingFile) {
1353+
nestedType = containingFile->lookupNestedType(memberName, baseType);
13641354
}
13651355
} else {
1366-
// Fault in extensions, then ask every serialized AST in the module.
1367-
(void)cast<NominalTypeDecl>(values.front())->getExtensions();
1368-
for (FileUnit *file : module->getFiles()) {
1356+
// Fault in extensions, then ask every file in the module.
1357+
ModuleDecl *extensionModule = M;
1358+
if (!extensionModule)
1359+
extensionModule = baseType->getModuleContext();
1360+
1361+
(void)baseType->getExtensions();
1362+
for (FileUnit *file : extensionModule->getFiles()) {
13691363
if (file == getFile())
13701364
continue;
1371-
auto *serializedFile = dyn_cast<SerializedASTFile>(file);
1372-
if (!serializedFile)
1373-
continue;
1374-
nestedType =
1375-
serializedFile->File.lookupNestedType(memberName,
1376-
values.front());
1365+
nestedType = file->lookupNestedType(memberName, baseType);
13771366
if (nestedType)
13781367
break;
13791368
}
@@ -1598,7 +1587,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
15981587
if (M != getAssociatedModule()) {
15991588
traceMsg.emplace("If you're seeing a crash here, check that your SDK "
16001589
"and dependencies match the versions used to build",
1601-
this);
1590+
*this);
16021591
}
16031592

16041593
if (values.empty()) {
@@ -4572,6 +4561,13 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
45724561
uint64_t contextData) {
45734562
using namespace decls_block;
45744563

4564+
PrettyStackTraceModuleFile traceModule("While reading from", *this);
4565+
PrettyStackTraceType trace(getAssociatedModule()->getASTContext(),
4566+
"finishing conformance for",
4567+
conformance->getType());
4568+
PrettyStackTraceDecl traceTo("... to", conformance->getProtocol());
4569+
++NumNormalProtocolConformancesCompleted;
4570+
45754571
// Find the conformance record.
45764572
BCOffsetRAII restoreOffset(DeclTypeCursor);
45774573
DeclTypeCursor.JumpToBit(contextData);

lib/Serialization/DeserializationErrors.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@
1717
#include "swift/AST/Module.h"
1818
#include "swift/Serialization/ModuleFormat.h"
1919
#include "llvm/Support/Error.h"
20+
#include "llvm/Support/PrettyStackTrace.h"
2021

2122
namespace swift {
23+
class ModuleFile;
24+
25+
StringRef getNameOfModule(const ModuleFile *);
26+
2227
namespace serialization {
2328

2429
class XRefTracePath {
@@ -313,6 +318,20 @@ class TypeError : public llvm::ErrorInfo<TypeError, DeclDeserializationError> {
313318
}
314319
};
315320

321+
class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
322+
const char *Action;
323+
const ModuleFile &MF;
324+
public:
325+
explicit PrettyStackTraceModuleFile(const char *action, ModuleFile &module)
326+
: Action(action), MF(module) {}
327+
explicit PrettyStackTraceModuleFile(ModuleFile &module)
328+
: PrettyStackTraceModuleFile("While reading from", module) {}
329+
330+
void print(raw_ostream &os) const override {
331+
os << Action << " \'" << getNameOfModule(&MF) << "'\n";
332+
}
333+
};
334+
316335
} // end namespace serialization
317336
} // end namespace swift
318337

0 commit comments

Comments
 (0)