Skip to content

[4.0] [ClangImporter] Add direct access for import-as-member types. #11013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,18 @@ class FileUnit : public DeclContext {
return nullptr;
}

/// Directly look for a nested type declared within this module inside the
/// given nominal type (including any extensions).
///
/// This is a fast-path hack to avoid circular dependencies in deserialization
/// and the Clang importer.
///
/// Private and fileprivate types should not be returned by this lookup.
virtual TypeDecl *lookupNestedType(Identifier name,
const NominalTypeDecl *parent) const {
return nullptr;
}

/// Find ValueDecls in the module and pass them to the given consumer object.
///
/// This does a simple local lookup, not recursively looking through imports.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/ClangImporter/ClangModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class ClangModuleUnit final : public LoadedFile {
DeclName name, NLKind lookupKind,
SmallVectorImpl<ValueDecl*> &results) const override;

virtual TypeDecl *
lookupNestedType(Identifier name,
const NominalTypeDecl *baseType) const override;

virtual void lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath,
VisibleDeclConsumer &consumer,
NLKind lookupKind) const override;
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ class ModuleFile : public LazyMemberLoader {

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

/// Searches the module's operators for one with the given name and fixity.
///
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ class SerializedASTFile final : public LoadedFile {

virtual TypeDecl *lookupLocalType(StringRef MangledName) const override;

virtual TypeDecl *
lookupNestedType(Identifier name,
const NominalTypeDecl *parent) const override;

virtual OperatorDecl *lookupOperator(Identifier name,
DeclKind fixity) const override;

Expand Down
89 changes: 87 additions & 2 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2428,6 +2428,91 @@ static bool isVisibleClangEntry(clang::ASTContext &ctx,
return true;
}

TypeDecl *
ClangModuleUnit::lookupNestedType(Identifier name,
const NominalTypeDecl *baseType) const {
// Special case for error code enums: try looking directly into the struct
// first. But only if it looks like a synthesized error wrapped struct.
if (name == getASTContext().Id_Code && !baseType->hasClangNode() &&
isa<StructDecl>(baseType) && !baseType->hasLazyMembers() &&
baseType->isChildContextOf(this)) {
auto *mutableBase = const_cast<NominalTypeDecl *>(baseType);
auto codeEnum = mutableBase->lookupDirect(name,/*ignoreNewExtensions*/true);
// Double-check that we actually have a good result. It's possible what we
// found is /not/ a synthesized error struct, but just something that looks
// like it. But if we still found a good result we should return that.
if (codeEnum.size() == 1 && isa<TypeDecl>(codeEnum.front()))
return cast<TypeDecl>(codeEnum.front());
if (codeEnum.size() > 1)
return nullptr;
// Otherwise, fall back and try via lookup table.
}

auto lookupTable = owner.findLookupTable(clangModule);
if (!lookupTable)
return nullptr;

auto baseTypeContext = owner.getEffectiveClangContext(baseType);
if (!baseTypeContext)
return nullptr;

auto &clangCtx = owner.getClangASTContext();

// FIXME: This is very similar to what's in Implementation::lookupValue and
// Implementation::loadAllMembers.
SmallVector<TypeDecl *, 2> results;
for (auto entry : lookupTable->lookup(name.str(),
baseTypeContext)) {
// If the entry is not visible, skip it.
if (!isVisibleClangEntry(clangCtx, entry)) continue;

auto clangDecl = entry.dyn_cast<clang::NamedDecl *>();
auto clangTypeDecl = dyn_cast_or_null<clang::TypeDecl>(clangDecl);
if (!clangTypeDecl)
continue;

clangTypeDecl = cast<clang::TypeDecl>(clangTypeDecl->getMostRecentDecl());

bool anyMatching = false;
TypeDecl *originalDecl = nullptr;
owner.forEachDistinctName(clangTypeDecl, [&](ImportedName newName,
ImportNameVersion nameVersion){
if (anyMatching)
return;
if (!newName.getDeclName().isSimpleName(name))
return;

auto decl = dyn_cast_or_null<TypeDecl>(
owner.importDeclReal(clangTypeDecl, nameVersion));
if (!decl)
return;

if (!originalDecl)
originalDecl = decl;
else if (originalDecl == decl)
return;

auto *importedContext = decl->getDeclContext()->
getAsNominalTypeOrNominalTypeExtensionContext();
if (importedContext != baseType)
return;

assert(decl->getFullName().matchesRef(name) &&
"importFullName behaved differently from importDecl");
results.push_back(decl);
anyMatching = true;
});
}

if (results.size() != 1) {
// It's possible that two types were import-as-member'd onto the same base
// type with the same name. In this case, fall back to regular lookup.
return nullptr;
}

return results.front();
}

void ClangImporter::loadExtensions(NominalTypeDecl *nominal,
unsigned previousGeneration) {
// Determine the effective Clang context for this Swift nominal type.
Expand Down Expand Up @@ -3124,7 +3209,7 @@ void ClangImporter::Implementation::lookupAllObjCMembers(
}

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

// Resolve the type.
if (auto typeResolver = getTypeResolver())
typeResolver->resolveDeclSignature(nominal);
typeResolver->resolveDeclSignature(const_cast<NominalTypeDecl *>(nominal));

// If it's an @objc entity, go look for it.
if (nominal->isObjC()) {
Expand Down
3 changes: 2 additions & 1 deletion lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
VisibleDeclConsumer &consumer);

/// Determine the effective Clang context for the given Swift nominal type.
EffectiveClangContext getEffectiveClangContext(NominalTypeDecl *nominal);
EffectiveClangContext
getEffectiveClangContext(const NominalTypeDecl *nominal);

/// Attempts to import the name of \p decl with each possible
/// ImportNameVersion. \p action will be called with each unique name.
Expand Down
60 changes: 28 additions & 32 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
STATISTIC(NumDeclsLoaded, "# of decls deserialized");
STATISTIC(NumMemberListsLoaded,
"# of nominals/extensions whose members were loaded");
STATISTIC(NumNormalProtocolConformancesLoaded,
"# of normal protocol conformances deserialized");
STATISTIC(NumNormalProtocolConformancesCompleted,
"# of normal protocol conformances completed");
STATISTIC(NumNestedTypeShortcuts,
"# of same-module nested types resolved without lookup");

Expand Down Expand Up @@ -108,18 +112,6 @@ namespace {
XRefTracePath::print(os, "\t");
}
};

class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
const char *Action;
const ModuleFile *MF;
public:
explicit PrettyStackTraceModuleFile(const char *action, ModuleFile *module)
: Action(action), MF(module) {}

void print(raw_ostream &os) const override {
os << Action << " \'" << getNameOfModule(MF) << "'\n";
}
};
} // end anonymous namespace

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

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

auto conformance = ctx.getConformance(conformingType, proto, SourceLoc(), dc,
ProtocolConformanceState::Incomplete);
Expand Down Expand Up @@ -1336,9 +1329,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
&blobData);
switch (recordID) {
case XREF_TYPE_PATH_PIECE: {
if (values.size() == 1) {
ModuleDecl *module = values.front()->getModuleContext();

if (values.size() == 1 && isa<NominalTypeDecl>(values.front())) {
// Fast path for nested types that avoids deserializing all
// members of the parent type.
IdentifierID IID;
Expand All @@ -1351,29 +1342,27 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
"If you're seeing a crash here, try passing "
"-Xfrontend -disable-serialization-nested-type-lookup-table"};

auto *baseType = cast<NominalTypeDecl>(values.front());
TypeDecl *nestedType = nullptr;
if (onlyInNominal) {
// Only look in the file containing the type itself.
const DeclContext *dc = values.front()->getDeclContext();
auto *serializedFile =
dyn_cast<SerializedASTFile>(dc->getModuleScopeContext());
if (serializedFile) {
nestedType =
serializedFile->File.lookupNestedType(memberName,
values.front());
auto *containingFile =
dyn_cast<FileUnit>(dc->getModuleScopeContext());
if (containingFile) {
nestedType = containingFile->lookupNestedType(memberName, baseType);
}
} else {
// Fault in extensions, then ask every serialized AST in the module.
(void)cast<NominalTypeDecl>(values.front())->getExtensions();
for (FileUnit *file : module->getFiles()) {
// Fault in extensions, then ask every file in the module.
ModuleDecl *extensionModule = M;
if (!extensionModule)
extensionModule = baseType->getModuleContext();

(void)baseType->getExtensions();
for (FileUnit *file : extensionModule->getFiles()) {
if (file == getFile())
continue;
auto *serializedFile = dyn_cast<SerializedASTFile>(file);
if (!serializedFile)
continue;
nestedType =
serializedFile->File.lookupNestedType(memberName,
values.front());
nestedType = file->lookupNestedType(memberName, baseType);
if (nestedType)
break;
}
Expand Down Expand Up @@ -1598,7 +1587,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
if (M != getAssociatedModule()) {
traceMsg.emplace("If you're seeing a crash here, check that your SDK "
"and dependencies match the versions used to build",
this);
*this);
}

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

PrettyStackTraceModuleFile traceModule("While reading from", *this);
PrettyStackTraceType trace(getAssociatedModule()->getASTContext(),
"finishing conformance for",
conformance->getType());
PrettyStackTraceDecl traceTo("... to", conformance->getProtocol());
++NumNormalProtocolConformancesCompleted;

// Find the conformance record.
BCOffsetRAII restoreOffset(DeclTypeCursor);
DeclTypeCursor.JumpToBit(contextData);
Expand Down
19 changes: 19 additions & 0 deletions lib/Serialization/DeserializationErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@
#include "swift/AST/Module.h"
#include "swift/Serialization/ModuleFormat.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/PrettyStackTrace.h"

namespace swift {
class ModuleFile;

StringRef getNameOfModule(const ModuleFile *);

namespace serialization {

class XRefTracePath {
Expand Down Expand Up @@ -313,6 +318,20 @@ class TypeError : public llvm::ErrorInfo<TypeError, DeclDeserializationError> {
}
};

class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
const char *Action;
const ModuleFile &MF;
public:
explicit PrettyStackTraceModuleFile(const char *action, ModuleFile &module)
: Action(action), MF(module) {}
explicit PrettyStackTraceModuleFile(ModuleFile &module)
: PrettyStackTraceModuleFile("While reading from", module) {}

void print(raw_ostream &os) const override {
os << Action << " \'" << getNameOfModule(&MF) << "'\n";
}
};

} // end namespace serialization
} // end namespace swift

Expand Down
Loading