From 4f0bd8bb4d1f101e24bce85d57e13a717cfdce25 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Mon, 13 Oct 2025 13:08:42 +0100 Subject: [PATCH] [cxx-interop] Improve performance of synthesized iterator conformances When conforming a C++ iterator type to the `UnsafeCxxInputIterator` protocol, we try to instantiate `operator==` for the given type, which is one of the requirements of the protocol. The logic that did name lookup was unintentionally running a global lookup for `==` across all of the visible Clang modules, which became a performance issue. This happened because ClangImporter was doing a name lookup in the fake `__ObjC` module instead of the actual owning Clang module for the iterator type. rdar://162125326 / resolves https://github.com/swiftlang/swift/issues/84733 --- .../ClangDerivedConformances.cpp | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 3991d985ae99b..b9052421ef012 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -142,7 +142,7 @@ static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id, // If no member operator was found, look for out-of-class definitions in the // same module. - auto module = decl->getModuleContext(); + auto module = decl->getModuleContextForNameLookup(); SmallVector nonMemberResults; module->lookupValue(id, NLKind::UnqualifiedLookup, nonMemberResults); for (const auto &nonMember : nonMemberResults) { @@ -273,8 +273,17 @@ instantiateTemplatedOperator(ClangImporter::Implementation &impl, best)) { case clang::OR_Success: { if (auto clangCallee = best->Function) { - auto lookupTable = impl.findLookupTable(classDecl); - addEntryToLookupTable(*lookupTable, clangCallee, impl.getNameImporter()); + // Declarations inside of a C++ namespace are added into two lookup + // tables: one for the __ObjC module, one for the actual owning Clang + // module of the decl. This is a hack that is meant to address the case + // when a namespace spans across multiple Clang modules. Mimic that + // behavior for the operator that we just instantiated. + auto lookupTable1 = impl.findLookupTable(classDecl); + addEntryToLookupTable(*lookupTable1, clangCallee, impl.getNameImporter()); + auto owningModule = impl.getClangOwningModule(classDecl); + auto lookupTable2 = impl.findLookupTable(owningModule); + if (lookupTable1 != lookupTable2) + addEntryToLookupTable(*lookupTable2, clangCallee, impl.getNameImporter()); return clangCallee; } break; @@ -377,8 +386,13 @@ static bool synthesizeCXXOperator(ClangImporter::Implementation &impl, equalEqualDecl->setBody(equalEqualBody); impl.synthesizedAndAlwaysVisibleDecls.insert(equalEqualDecl); - auto lookupTable = impl.findLookupTable(classDecl); - addEntryToLookupTable(*lookupTable, equalEqualDecl, impl.getNameImporter()); + auto lookupTable1 = impl.findLookupTable(classDecl); + addEntryToLookupTable(*lookupTable1, equalEqualDecl, impl.getNameImporter()); + auto owningModule = impl.getClangOwningModule(classDecl); + auto lookupTable2 = impl.findLookupTable(owningModule); + if (lookupTable1 != lookupTable2) + addEntryToLookupTable(*lookupTable2, equalEqualDecl, + impl.getNameImporter()); return true; }