[cxx-interop] Clang member lookup should not look into Swift extensions

Calling `NominalTypeDecl::lookupDirect` triggers deserialization of Swift extensions for the type. `ClangRecordMemberLookup` shouldn't assume it is allowed to deserialize Swift extensions for the given C++ type: there might be extensions which reference the module that is currently being imported, which causes circular request dependency errors.
This commit is contained in:
Egor Zhdan
2022-08-04 15:53:34 +01:00
parent df2d938604
commit 51a1176d90
5 changed files with 55 additions and 5 deletions

View File

@@ -184,6 +184,11 @@ public:
/// Imports a clang decl directly, rather than looking up its name.
virtual Decl *importDeclDirectly(const clang::NamedDecl *decl) = 0;
/// Imports a clang decl from a base class, cloning it for \param newContext
/// if it wasn't cloned for this specific context before.
virtual ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) = 0;
/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
virtual void diagnoseTopLevelValue(const DeclName &name) = 0;

View File

@@ -531,6 +531,9 @@ public:
/// Imports a clang decl directly, rather than looking up it's name.
Decl *importDeclDirectly(const clang::NamedDecl *decl) override;
ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) override;
/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
void diagnoseTopLevelValue(const DeclName &name) override;

View File

@@ -4935,11 +4935,12 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
// Find the results that are actually a member of "recordDecl".
TinyPtrVector<ValueDecl *> result;
ClangModuleLoader *clangModuleLoader = ctx.getClangModuleLoader();
for (auto found : allResults) {
auto named = found.get<clang::NamedDecl *>();
if (dyn_cast<clang::Decl>(named->getDeclContext()) ==
recordDecl->getClangDecl()) {
if (auto import = ctx.getClangModuleLoader()->importDeclDirectly(named))
if (auto import = clangModuleLoader->importDeclDirectly(named))
result.push_back(cast<ValueDecl>(import));
}
}
@@ -4955,16 +4956,32 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
continue;
auto *baseRecord = baseType->getAs<clang::RecordType>()->getDecl();
if (auto import =
ctx.getClangModuleLoader()->importDeclDirectly(baseRecord)) {
if (auto import = clangModuleLoader->importDeclDirectly(baseRecord)) {
// If we are looking up the base class, go no further. We will have
// already found it during the other lookup.
if (cast<ValueDecl>(import)->getName() == name)
continue;
auto baseResults = cast<NominalTypeDecl>(import)->lookupDirect(name);
// Add Clang members that are imported lazily.
auto baseResults = evaluateOrDefault(
ctx.evaluator,
ClangRecordMemberLookup({cast<NominalTypeDecl>(import), name}), {});
// Add members that are synthesized eagerly, such as subscripts.
for (auto member :
cast<NominalTypeDecl>(import)->getCurrentMembersWithoutLoading()) {
if (auto namedMember = dyn_cast<ValueDecl>(member)) {
if (namedMember->hasName() &&
namedMember->getName().getBaseName() == name &&
// Make sure we don't add duplicate entries, as that would
// wrongly imply that lookup is ambiguous.
!llvm::is_contained(baseResults, namedMember)) {
baseResults.push_back(namedMember);
}
}
}
for (auto foundInBase : baseResults) {
if (auto newDecl = cloneBaseMemberDecl(foundInBase, recordDecl)) {
if (auto newDecl = clangModuleLoader->importBaseMemberDecl(
foundInBase, recordDecl)) {
result.push_back(newDecl);
}
}
@@ -5761,6 +5778,18 @@ Decl *ClangImporter::importDeclDirectly(const clang::NamedDecl *decl) {
return Impl.importDecl(decl, Impl.CurrentVersion);
}
ValueDecl *ClangImporter::importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) {
// Make sure we don't clone the decl again for this class, as that would
// result in multiple definitions of the same symbol.
std::pair<ValueDecl *, DeclContext *> key = {decl, newContext};
if (!Impl.clonedBaseMembers.count(key)) {
ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext);
Impl.clonedBaseMembers[key] = cloned;
}
return Impl.clonedBaseMembers[key];
}
void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
Impl.diagnoseTopLevelValue(name);
}

View File

@@ -621,6 +621,10 @@ public:
llvm::DenseSet<clang::FunctionDecl *>>>>
cxxMethods;
// Keep track of the decls that were already cloned for this specific class.
llvm::DenseMap<std::pair<ValueDecl *, DeclContext *>, ValueDecl *>
clonedBaseMembers;
// Cache for already-specialized function templates and any thunks they may
// have.
llvm::DenseMap<clang::FunctionDecl *, ValueDecl *>

View File

@@ -0,0 +1,9 @@
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-experimental-cxx-interop
import Functions
extension Base {
public func swiftFunc() {}
}
Derived().swiftFunc() // expected-error {{value of type 'Derived' has no member 'swiftFunc'}}