From ff65857c1be2219a65be45e922ac5cf446cf329f Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 18 Dec 2025 16:13:04 -0800 Subject: [PATCH 1/6] [cxx-interop] [NFC] Make getClangOwningModule a free-standing function Remove the ClangImporter::Implementation::getClangOwningModule() method, and replaces all call sites with the static free-standing function it calls, now exposed in the swift::importer namespace as an internal API. This makes it easier to use in contexts where an Implementation instance is not readily available. Also, clone the documentation to the public ClangImporter API of the same name. --- include/swift/AST/ClangModuleLoader.h | 4 ++++ include/swift/ClangImporter/ClangImporter.h | 6 ++++++ lib/ClangImporter/ClangDerivedConformances.cpp | 7 ++++--- lib/ClangImporter/ClangImporter.cpp | 12 ++++-------- lib/ClangImporter/ImportMacro.cpp | 3 ++- lib/ClangImporter/ImporterImpl.h | 11 +++++++---- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index c01a76a31d2a0..ed45cb972e53a 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -334,6 +334,10 @@ class ClangModuleLoader : public ModuleLoader { virtual SwiftLookupTable * findLookupTable(const clang::Module *clangModule) = 0; + /// Returns the module \p Node comes from, or \c nullptr if \p Node does not + /// have a valid owning module. + /// + /// Note that \p Node cannot itself be a clang::Module. virtual const clang::Module *getClangOwningModule(ClangNode Node) const = 0; virtual DeclName diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 81da1291ff293..1685595c5028c 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -491,7 +491,13 @@ class ClangImporter final : public ClangModuleLoader { bool dumpPrecompiledModule(StringRef modulePath, StringRef outputPath); bool runPreprocessor(StringRef inputPath, StringRef outputPath); + + /// Returns the module \p Node comes from, or \c nullptr if \p Node does not + /// have a valid owning module. + /// + /// Note that \p Node cannot itself be a clang::Module. const clang::Module *getClangOwningModule(ClangNode Node) const override; + bool hasTypedef(const clang::Decl *typeDecl) const; void verifyAllModules() override; diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 4608437ec89e3..4e8d1d661fa63 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -304,7 +304,7 @@ instantiateTemplatedOperator(ClangImporter::Implementation &impl, // behavior for the operator that we just instantiated. auto lookupTable1 = impl.findLookupTable(classDecl); addEntryToLookupTable(*lookupTable1, clangCallee, impl.getNameImporter()); - auto owningModule = impl.getClangOwningModule(classDecl); + auto owningModule = importer::getClangOwningModule(classDecl, clangCtx); auto lookupTable2 = impl.findLookupTable(owningModule); if (lookupTable1 != lookupTable2) addEntryToLookupTable(*lookupTable2, clangCallee, impl.getNameImporter()); @@ -412,7 +412,7 @@ static bool synthesizeCXXOperator(ClangImporter::Implementation &impl, impl.synthesizedAndAlwaysVisibleDecls.insert(equalEqualDecl); auto lookupTable1 = impl.findLookupTable(classDecl); addEntryToLookupTable(*lookupTable1, equalEqualDecl, impl.getNameImporter()); - auto owningModule = impl.getClangOwningModule(classDecl); + auto owningModule = importer::getClangOwningModule(classDecl, clangCtx); auto lookupTable2 = impl.findLookupTable(owningModule); if (lookupTable1 != lookupTable2) addEntryToLookupTable(*lookupTable2, equalEqualDecl, @@ -1259,7 +1259,8 @@ void swift::deriveAutomaticCxxConformances( // // We will still attempt to synthesize to account for scenarios where the // module specification is missing altogether. - if (auto *clangModule = Impl.getClangOwningModule(result->getClangNode()); + if (auto *clangModule = importer::getClangOwningModule( + result->getClangNode(), Impl.getClangASTContext()); clangModule && !requiresCPlusPlus(clangModule)) return; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index b6125283435ff..68e33e4c5dba9 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3255,8 +3255,9 @@ static bool isDeclaredInModule(const ClangModuleUnit *ModuleFilter, return ModuleFilter == ContainingUnit; } -static const clang::Module * -getClangOwningModule(ClangNode Node, const clang::ASTContext &ClangCtx) { +const clang::Module * +importer::getClangOwningModule(ClangNode Node, + const clang::ASTContext &ClangCtx) { assert(!Node.getAsModule() && "not implemented for modules"); if (const clang::Decl *D = Node.getAsDecl()) { @@ -4315,12 +4316,7 @@ const clang::CompilerInstance &ClangImporter::getClangInstance() const { } const clang::Module *ClangImporter::getClangOwningModule(ClangNode Node) const { - return Impl.getClangOwningModule(Node); -} - -const clang::Module * -ClangImporter::Implementation::getClangOwningModule(ClangNode Node) const { - return ::getClangOwningModule(Node, getClangASTContext()); + return importer::getClangOwningModule(Node, Impl.getClangASTContext()); } bool ClangImporter::hasTypedef(const clang::Decl *typeDecl) const { diff --git a/lib/ClangImporter/ImportMacro.cpp b/lib/ClangImporter/ImportMacro.cpp index f9f45e73851b7..683209abc4cc7 100644 --- a/lib/ClangImporter/ImportMacro.cpp +++ b/lib/ClangImporter/ImportMacro.cpp @@ -954,7 +954,8 @@ ValueDecl *ClangImporter::Implementation::importMacro(Identifier name, // result. DeclContext *DC; - if (const clang::Module *module = getClangOwningModule(macroNode)) { + if (const clang::Module *module = + importer::getClangOwningModule(macroNode, getClangASTContext())) { // Get the parent module because currently we don't model Clang submodules // in Swift. DC = getWrapperForModule(module->getTopLevelModule()); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 881efeff25634..699abf82b99c0 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -970,10 +970,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation ClangModuleUnit *getClangModuleForDecl(const clang::Decl *D, bool allowForwardDeclaration = false); - /// Returns the module \p Node comes from, or \c nullptr if \p Node does not - /// have a valid owning module. - const clang::Module *getClangOwningModule(ClangNode Node) const; - /// Whether NSUInteger can be imported as Int in certain contexts. If false, /// should always be imported as UInt. static bool shouldAllowNSUIntegerAsInt(bool isFromSystemModule, @@ -2222,6 +2218,13 @@ getImplicitObjectParamAnnotation(const clang::FunctionDecl *FD) { return nullptr; } +/// Returns the module \p Node comes from, or \c nullptr if \p Node does not +/// have a valid owning module. +/// +/// Note that \p Node cannot itself be a clang::Module. +const clang::Module *getClangOwningModule(ClangNode Node, + const clang::ASTContext &ClangCtx); + } // end namespace importer } // end namespace swift From e11b96f934cb646cb50cf61fb3bc4c28dc09c828 Mon Sep 17 00:00:00 2001 From: John Hui Date: Wed, 17 Dec 2025 17:39:46 -0800 Subject: [PATCH 2/6] [cxx-interop] [NFC] Do not check result from cast<> This cast was previously refactored from llvm::PointerUnion::get, which both abort upon an invalid cast rather than returning a nullptr, so there is no point checking this in a condition. --- lib/ClangImporter/ClangImporter.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 68e33e4c5dba9..9d7c32d7e42ad 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5123,17 +5123,16 @@ void ClangImporter::Implementation::diagnoseMemberValue( forEachLookupTable([&](SwiftLookupTable &table) -> bool { for (const auto &entry : table.lookup(name.getBaseName(), EffectiveClangContext(container))) { - if (clang::NamedDecl *nd = cast(entry)) { - // We are only interested in members of a particular context, - // skip other contexts. - if (nd->getDeclContext() != container) - continue; - - diagnoseTargetDirectly( - importDiagnosticTargetFromLookupTableEntry(entry)); - } // If the entry is not a NamedDecl, it is a form of macro, which cannot be - // a member value. + // a member value, so it is safe to cast here. + clang::NamedDecl *nd = cast(entry); + + // We are only interested in members of a particular context, + // skip other contexts. + if (nd->getDeclContext() != container) + continue; + + diagnoseTargetDirectly(importDiagnosticTargetFromLookupTableEntry(entry)); } return false; }); From d216ceb4664c2ee1ba74874bb4439421b36231b5 Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 18 Dec 2025 19:01:12 -0800 Subject: [PATCH 3/6] [cxx-interop] [NFC] Move ClangDirectLookupRequest to ClangLookup.cpp ClangImporter.cpp is so large that it is getting difficult to navigate. Start to migrate lookup-related code to a separate file, ClangLookup.cpp. This first patch moves ClangDirectLookupRequest and related helpers to a new file, ClangLookup.cpp. This request looks for decls imported from Clang according to their Swift name, from mappings stored in the SwiftLookupTable that ClangImporter writes into (its copy of ) the Clang modules it imports. This request also includes separate handling of lookup in class template specializations, which are not in the SwiftLookupTable. At this time of refactoring, lookup works like this. If we are looking in some struct Foo for a member Bar, we look for _everything_ named "Bar" in the module that owns Foo, and then filter out everything that isn't a member of Foo (according to DeclContext). This is inefficient, but correctly accounts for members that have been "injected" into structs via the SIWFT_NAME, and also accounts for other name mappings done by the NameImporter. For the inevitable future Git archaeologist, what follows are the commits whose blame is destroyed by this refactoring: b8e52a7a [cxx-interop] Lazily import members of Clang namespaces and records via requests. 0ca8dd36 [nfc][cxx-interop] Add three requests `ClangDirectLookupRequest`, `CXXNamespaceMemberLookup`, and `ClangRecordMemberLookup`. 1089959b [interop] clang name lookup should find declarations in inline namespaces c952fc17 [cxx-interop] Fix lookup of class template instantiations 0eed1a94 [cxx-interop] correctly add and lookup enumerators added to enums that correspond to namespaces --- lib/ClangImporter/CMakeLists.txt | 1 + lib/ClangImporter/ClangImporter.cpp | 91 ------------------ lib/ClangImporter/ClangLookup.cpp | 137 ++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 91 deletions(-) create mode 100644 lib/ClangImporter/ClangLookup.cpp diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 055cf8942a4a8..34f29a962c3f5 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -14,6 +14,7 @@ add_swift_host_library(swiftClangImporter STATIC ClangImporter.cpp ClangImporterRequests.cpp ClangIncludePaths.cpp + ClangLookup.cpp ClangModuleDependencyScanner.cpp ClangSourceBufferImporter.cpp SwiftDeclSynthesizer.cpp diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 9d7c32d7e42ad..ea59698df08ef 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5201,97 +5201,6 @@ bool ClangImporter::Implementation::emitDiagnosticsForTarget( return ImportDiagnostics[target].size(); } -static SmallVector -lookupInClassTemplateSpecialization( - ASTContext &ctx, const clang::ClassTemplateSpecializationDecl *clangDecl, - DeclName name) { - // TODO: we could make this faster if we can cache class templates in the - // lookup table as well. - // Import all the names to figure out which ones we're looking for. - SmallVector found; - for (auto member : clangDecl->decls()) { - auto namedDecl = dyn_cast(member); - if (!namedDecl) - continue; - - auto memberName = ctx.getClangModuleLoader()->importName(namedDecl); - if (!memberName) - continue; - - // Use the base names here because *sometimes* our input name won't have - // any arguments. - if (name.getBaseName().compare(memberName.getBaseName()) == 0) - found.push_back(namedDecl); - } - - return found; -} - -static bool isDirectLookupMemberContext(const clang::Decl *foundClangDecl, - const clang::Decl *memberContext, - const clang::Decl *parent) { - if (memberContext->getCanonicalDecl() == parent->getCanonicalDecl()) - return true; - if (auto namespaceDecl = dyn_cast(memberContext)) { - if (namespaceDecl->isInline()) { - if (auto memberCtxParent = - dyn_cast(namespaceDecl->getParent())) - return isDirectLookupMemberContext(foundClangDecl, memberCtxParent, - parent); - } - } - // Enum constant decl can be found in the parent context of the enum decl. - if (auto *ED = dyn_cast(memberContext)) { - if (isa(foundClangDecl)) { - if (auto *firstDecl = dyn_cast(ED->getDeclContext())) - return firstDecl->getCanonicalDecl() == parent->getCanonicalDecl(); - } - } - return false; -} - -SmallVector -ClangDirectLookupRequest::evaluate(Evaluator &evaluator, - ClangDirectLookupDescriptor desc) const { - auto &ctx = desc.decl->getASTContext(); - auto *clangDecl = desc.clangDecl; - // Class templates aren't in the lookup table. - if (auto spec = dyn_cast(clangDecl)) - return lookupInClassTemplateSpecialization(ctx, spec, desc.name); - - SwiftLookupTable *lookupTable = nullptr; - if (isa(clangDecl)) { - // DeclContext of a namespace imported into Swift is the __ObjC module. - lookupTable = ctx.getClangModuleLoader()->findLookupTable(nullptr); - } else { - auto *clangModule = - getClangOwningModule(clangDecl, clangDecl->getASTContext()); - lookupTable = ctx.getClangModuleLoader()->findLookupTable(clangModule); - } - - auto foundDecls = lookupTable->lookup( - SerializedSwiftName(desc.name.getBaseName()), EffectiveClangContext()); - // Make sure that `clangDecl` is the parent of all the members we found. - SmallVector filteredDecls; - llvm::copy_if(foundDecls, std::back_inserter(filteredDecls), - [clangDecl](SwiftLookupTable::SingleEntry decl) { - auto foundClangDecl = decl.dyn_cast(); - if (!foundClangDecl) - return false; - auto first = foundClangDecl->getDeclContext(); - auto second = cast(clangDecl); - if (auto firstDecl = dyn_cast(first)) { - if (auto secondDecl = dyn_cast(second)) - return isDirectLookupMemberContext(foundClangDecl, - firstDecl, secondDecl); - else - return false; - } - return first == second; - }); - return filteredDecls; -} - namespace { /// Collects name lookup results into the given tiny vector, for use in the /// various Clang importer lookup routines. diff --git a/lib/ClangImporter/ClangLookup.cpp b/lib/ClangImporter/ClangLookup.cpp new file mode 100644 index 0000000000000..e4808199c9994 --- /dev/null +++ b/lib/ClangImporter/ClangLookup.cpp @@ -0,0 +1,137 @@ +//===--- ClangLookup.cpp - Lookup in entities imported from Clang ---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file contains facilities for name lookup in entites imported from Clang +// +//===----------------------------------------------------------------------===// + +#include "ImporterImpl.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Builtins.h" +#include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/ConcreteDeclRef.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Evaluator.h" +#include "swift/AST/Module.h" +#include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/Types.h" +#include "swift/Basic/LLVM.h" +#include "swift/ClangImporter/ClangImporter.h" +#include "swift/ClangImporter/ClangImporterRequests.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/Subsystems.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/Type.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +using namespace swift; + +static SmallVector +lookupInClassTemplateSpecialization( + ASTContext &ctx, const clang::ClassTemplateSpecializationDecl *clangDecl, + DeclName name) { + // TODO: we could make this faster if we can cache class templates in the + // lookup table as well. + // Import all the names to figure out which ones we're looking for. + SmallVector found; + for (auto member : clangDecl->decls()) { + auto *namedDecl = dyn_cast(member); + if (!namedDecl) + continue; + + auto memberName = ctx.getClangModuleLoader()->importName(namedDecl); + if (!memberName) + continue; + + // Use the base names here because *sometimes* our input name won't have + // any arguments. + if (name.getBaseName().compare(memberName.getBaseName()) == 0) + found.push_back(namedDecl); + } + + return found; +} + +static bool isDirectLookupMemberContext(const clang::Decl *foundClangDecl, + const clang::Decl *memberContext, + const clang::Decl *parent) { + if (memberContext->getCanonicalDecl() == parent->getCanonicalDecl()) + return true; + if (auto *namespaceDecl = dyn_cast(memberContext)) { + if (namespaceDecl->isInline()) { + if (auto *memberCtxParent = + dyn_cast(namespaceDecl->getParent())) + return isDirectLookupMemberContext(foundClangDecl, memberCtxParent, + parent); + } + } + // Enum constant decl can be found in the parent context of the enum decl. + if (auto *ED = dyn_cast(memberContext)) { + if (isa(foundClangDecl)) { + if (auto *firstDecl = dyn_cast(ED->getDeclContext())) + return firstDecl->getCanonicalDecl() == parent->getCanonicalDecl(); + } + } + return false; +} + +SmallVector +ClangDirectLookupRequest::evaluate(Evaluator &evaluator, + ClangDirectLookupDescriptor desc) const { + auto &ctx = desc.decl->getASTContext(); + auto *clangDecl = desc.clangDecl; + // Class templates aren't in the lookup table. + if (auto *spec = dyn_cast(clangDecl)) + return lookupInClassTemplateSpecialization(ctx, spec, desc.name); + + SwiftLookupTable *lookupTable; + if (isa(clangDecl)) { + // DeclContext of a namespace imported into Swift is the __ObjC module. + lookupTable = ctx.getClangModuleLoader()->findLookupTable(nullptr); + } else { + auto *clangModule = + importer::getClangOwningModule(clangDecl, clangDecl->getASTContext()); + lookupTable = ctx.getClangModuleLoader()->findLookupTable(clangModule); + } + + auto foundDecls = lookupTable->lookup( + SerializedSwiftName(desc.name.getBaseName()), EffectiveClangContext()); + // Make sure that `clangDecl` is the parent of all the members we found. + SmallVector filteredDecls; + llvm::copy_if(foundDecls, std::back_inserter(filteredDecls), + [clangDecl](SwiftLookupTable::SingleEntry decl) { + auto *foundClangDecl = decl.dyn_cast(); + if (!foundClangDecl) + return false; + auto *first = foundClangDecl->getDeclContext(); + auto *second = cast(clangDecl); + if (auto *firstDecl = dyn_cast(first)) { + if (auto *secondDecl = dyn_cast(second)) { + return isDirectLookupMemberContext(foundClangDecl, + firstDecl, secondDecl); + } + return false; + } + return first == second; + }); + return filteredDecls; +} From 619c861e34ea4312aca8899efe1c597948965091 Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 18 Dec 2025 19:48:47 -0800 Subject: [PATCH 4/6] [cxx-interop] [NFC] Move CXXNamespaceMemberLookup to ClangLookup.cpp ClangImporter.cpp is so large that it is getting difficult to navigate. Migrate lookup-related code to a separate file, ClangLookup.cpp. This patch moves CXXNamespaceMemberLookup and its related helper. This request looks for decls in a namespace, by performing a ClangDirectLookupRequest in each redeclaration of that namespace. The results are imported to convert them to swift::ValueDecls. For the inevitable future Git archaeologist, what follows are the commits whose blame is destroyed by this refactoring: b8e52a7a [cxx-interop] Lazily import members of Clang namespaces and records via requests. 0ca8dd36 [nfc][cxx-interop] Add three requests `ClangDirectLookupRequest`, `CXXNamespaceMemberLookup`, and `ClangRecordMemberLookup`. 0c7b1cee [cxx-interop] serialize x-refs for class template specializations 84a4a8be [Importer] Ensure that we can see macro-expanded declarations in C++ namespaces --- lib/ClangImporter/ClangImporter.cpp | 33 ------------- lib/ClangImporter/ClangLookup.cpp | 75 +++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index ea59698df08ef..5c99d995215ba 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5237,39 +5237,6 @@ namespace { }; } -TinyPtrVector CXXNamespaceMemberLookup::evaluate( - Evaluator &evaluator, CXXNamespaceMemberLookupDescriptor desc) const { - EnumDecl *namespaceDecl = desc.namespaceDecl; - DeclName name = desc.name; - auto *clangNamespaceDecl = - cast(namespaceDecl->getClangDecl()); - auto &ctx = namespaceDecl->getASTContext(); - - TinyPtrVector result; - CollectLookupResults collector(name, result); - - llvm::SmallPtrSet importedDecls; - for (auto redecl : clangNamespaceDecl->redecls()) { - auto allResults = evaluateOrDefault( - ctx.evaluator, ClangDirectLookupRequest({namespaceDecl, redecl, name}), - {}); - - for (auto found : allResults) { - auto clangMember = cast(found); - auto it = importedDecls.insert(clangMember); - // Skip over members already found during lookup in - // prior redeclarations. - if (!it.second) - continue; - if (auto import = - ctx.getClangModuleLoader()->importDeclDirectly(clangMember)) - collector.add(cast(import)); - } - } - - return result; -} - static const llvm::StringMap> STLConditionalParams{ {"basic_string", {0}}, {"vector", {0}}, diff --git a/lib/ClangImporter/ClangLookup.cpp b/lib/ClangImporter/ClangLookup.cpp index e4808199c9994..c2ccdc320ff16 100644 --- a/lib/ClangImporter/ClangLookup.cpp +++ b/lib/ClangImporter/ClangLookup.cpp @@ -22,16 +22,22 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Evaluator.h" +#include "swift/AST/ImportCache.h" #include "swift/AST/Module.h" +#include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/SourceFile.h" +#include "swift/AST/Type.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/LLVM.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterRequests.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Subsystems.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclTemplate.h" @@ -42,9 +48,46 @@ #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include using namespace swift; +namespace { +/// Collects name lookup results into the given tiny vector, for use in the +/// various ClangImporter lookup routines. +class CollectLookupResults { + DeclName name; + TinyPtrVector &result; + +public: + CollectLookupResults(DeclName name, TinyPtrVector &result) + : name(name), result(result) {} + + void add(ValueDecl *imported) { + result.push_back(imported); + + // Expand any macros introduced by the Clang importer. + imported->visitAuxiliaryDecls([&](Decl *decl) { + auto valueDecl = dyn_cast(decl); + if (!valueDecl) + return; + + // Bail out if the auxiliary decl was not produced by a macro. + auto module = decl->getDeclContext()->getParentModule(); + auto *sf = module->getSourceFileContainingLocation(decl->getLoc()); + if (!sf || sf->Kind != SourceFileKind::MacroExpansion) + return; + + // Only produce results that match the requested name. + if (!valueDecl->getName().matchesRef(name)) + return; + + result.push_back(valueDecl); + }); + } +}; +} // anonymous namespace + static SmallVector lookupInClassTemplateSpecialization( ASTContext &ctx, const clang::ClassTemplateSpecializationDecl *clangDecl, @@ -135,3 +178,35 @@ ClangDirectLookupRequest::evaluate(Evaluator &evaluator, }); return filteredDecls; } + +TinyPtrVector CXXNamespaceMemberLookup::evaluate( + Evaluator &evaluator, CXXNamespaceMemberLookupDescriptor desc) const { + EnumDecl *namespaceDecl = desc.namespaceDecl; + DeclName name = desc.name; + auto *clangNamespaceDecl = + cast(namespaceDecl->getClangDecl()); + auto &ctx = namespaceDecl->getASTContext(); + + TinyPtrVector result; + CollectLookupResults collector(name, result); + + llvm::SmallPtrSet importedDecls; + for (auto redecl : clangNamespaceDecl->redecls()) { + auto allResults = evaluateOrDefault( + ctx.evaluator, ClangDirectLookupRequest({namespaceDecl, redecl, name}), + {}); + + for (auto found : allResults) { + auto clangMember = cast(found); + auto it = importedDecls.insert(clangMember); + // Skip over members already found during lookup in prior redeclarations. + if (!it.second) + continue; + if (auto import = + ctx.getClangModuleLoader()->importDeclDirectly(clangMember)) + collector.add(cast(import)); + } + } + + return result; +} From fb57fd8b5e26c6d8d9219a56053f77b560627c60 Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 18 Dec 2025 23:13:12 -0800 Subject: [PATCH 5/6] [cxx-interop] [NFC] Move ClangRecordMemberLookup to ClangLookup.cpp ClangImporter.cpp is so large that it is getting difficult to navigate. Migrate lookup-related code to a separate file, ClangLookup.cpp. This patch moves the ClangRecordMemberLookup request and the base member cloning logic to ClangLookup.cpp. This request looks for members of a given (Swift) name in a (imported) Clang record, and any of its base classes. The results are imported to convert them to swift::ValueDecls. Members found in base classes are not directly added to the inherting class, because Swift does not model inheritance for imported structs. Instead, they are "cloned" from base members via a synthesized accessor that is constructed lazily. This indirection delegates the low-level details of the base member access to Clang. At this time of refactoring, lookup for automatically synthesized members like .pointee and .successor relies on the RecordDecl import logic being eager. Inherited lookup also has known limitations around inherited C-style enums and static member functions. The lookup logic for non-public members is gated under ImportNonPublicCxxMembers feature, to mitigate issues that may arise from exposing ClangImporter to private decls that it cannot yet robustly handle. For instance, it does not deduplicate non-public members that seem to appear twice during lookup due to UsingShadowDecl. Also, the inherited lookup logic does not leverage the existing facilities from Clang, because it needs to handle Swift-specific details like including members injected by SWIFT_NAME and dropping constructors looked up from foreign reference types. For member functions, it performs some member deduplication according to parameter count, to avoid creating spurious ambiguity. For the inevitable future Git archaeologist, what follows are the commits whose blame is destroyed by this refactoring: b8e52a7a [cxx-interop] Lazily import members of Clang namespaces and records via requests. bb00e8dc [cxx-interop] Rudimentary support for importing base classes. 0ca8dd36 [nfc][cxx-interop] Add three requests `ClangDirectLookupRequest`, `CXXNamespaceMemberLookup`, and `ClangRecordMemberLookup`. 2e0398f8 [cxx-interop] Add support for subscripts in base classes. 336cd293 [cxx-interop] Add support for calling members of base classes on UFOs. e73bb6fd [cxx-interop] Import attributes on inherited C++ methods 3805828a [cxx-interop] Add `CxxShim` library; move `__swift_interopStaticCast` into it. 51a1176d [cxx-interop] Clang member lookup should not look into Swift extensions 109d44c5 [cxx-interop] Emit LValue type in a base subscript assignment expression 8df9fca0 [cxx-interop] Don't import constructors of foreign reference types. a1033254 [cxx-interop] Do not add base class members that cause lookup ambiguities with a derived class member of the same name ba5b1bab [cxx-interop] Use a synthesized C++ method when invoking a base method from a derived class synthesized method 41504502 [cxx-interop] Use a synthesized C++ method when accessing a base field or subscript from a derived class synthesized method f7ce9aa3 [cxx-interop] Synthesized derived-to-base field getter should copy out a retainable FRT value f9bf9575 [cxx-interop] Do not import inherited methods with rvalue this bacc58e0 [cxx-interop] provide correct referential access to non-copyable base fields from a derived value type d3460cb2 [cxx-interop] Do not crash when synthesizing a base method call 16a8ae42 [cxx-interop] fix the use of '.pointee' with address accessors for derived-to-base synthesized accessors 02964480 [cxx-interop] fix the use of '.pointee' with getter accessor for derived-to-base synthesized accessor 44d7d06d Don't lie about the intermediate result type when synthesizing a call to a function that returns UnsafeMutablePointer. 6140ba10 [cxx-interop] Do not try to use `UnsafePointer` for non-copyable `T` when adding base member accessors 5ae2f6bd CxxInterop: use Unsafe*Pointer for move-only b5095514 [cxx-interop] Clone all of the attributes from base method correctly 796075ab [cxx-interop] Fix spurious ambiguous member lookup 1341516d [cxx-interop] Fix spurious ambiguous member lookup for eagerly imported members (#78673) be73254c [cxx-interop] Import private members 66c2e2c5 [cxx-interop] Import non-public inherited members edc74201 [cxx-interop] Make experimental flag ImportNonPublicCxxMembers e3618dd7 [Clang importer] Report auxiliary decls from C++ member lookup 84a4a8be [Importer] Ensure that we can see macro-expanded declarations in C++ namespaces bbf92fd6 [cxx-interop] Import non-public members of SWIFT_PRIVATE_FILEID-annotated classes e8bcc523 [cxx-interop] Fix access check for nested private C++ enums 1f2107f3 [cxx-interop] Avoid unchecked recursion when importing C++ classes with circular inheritance 79227e7a [cxx-interop] Fix ambiguous methods in long chains of inheritance 4de92656 [cxx-interop] Fix unqualified name lookup failure 585ca5e2 [cxx-interop] Adding swift_name attributes to virtual methods overrides 72be0e08 [Clang importer] Import incomplete SWIFT_SHARED_REFERENCE types 563c0144 [cxx-interop] Fix inherited nested types --- lib/ClangImporter/ClangImporter.cpp | 1117 --------------------------- lib/ClangImporter/ClangLookup.cpp | 1111 ++++++++++++++++++++++++++ 2 files changed, 1111 insertions(+), 1117 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 5c99d995215ba..030e21528a08d 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5201,42 +5201,6 @@ bool ClangImporter::Implementation::emitDiagnosticsForTarget( return ImportDiagnostics[target].size(); } -namespace { - /// Collects name lookup results into the given tiny vector, for use in the - /// various Clang importer lookup routines. - class CollectLookupResults { - DeclName name; - TinyPtrVector &result; - - public: - CollectLookupResults(DeclName name, TinyPtrVector &result) - : name(name), result(result) { } - - void add(ValueDecl *imported) { - result.push_back(imported); - - // Expand any macros introduced by the Clang importer. - imported->visitAuxiliaryDecls([&](Decl *decl) { - auto valueDecl = dyn_cast(decl); - if (!valueDecl) - return; - - // Bail out if the auxiliary decl was not produced by a macro. - auto module = decl->getDeclContext()->getParentModule(); - auto *sf = module->getSourceFileContainingLocation(decl->getLoc()); - if (!sf || sf->Kind != SourceFileKind::MacroExpansion) - return; - - // Only produce results that match the requested name. - if (!valueDecl->getName().matchesRef(name)) - return; - - result.push_back(valueDecl); - }); - } - }; -} - static const llvm::StringMap> STLConditionalParams{ {"basic_string", {0}}, {"vector", {0}}, @@ -5476,1045 +5440,6 @@ SourceLoc swift::extractNearestSourceLoc(EscapabilityLookupDescriptor) { return SourceLoc(); } -// Just create a specialized function decl for "__swift_interopStaticCast" -// using the types base and derived. -static -DeclRefExpr *getInteropStaticCastDeclRefExpr(ASTContext &ctx, - const clang::Module *owningModule, - Type base, Type derived) { - if (base->isForeignReferenceType() && derived->isForeignReferenceType()) { - base = base->wrapInPointer(PTK_UnsafePointer); - derived = derived->wrapInPointer(PTK_UnsafePointer); - } - - // Lookup our static cast helper function in the C++ shim module. - auto wrapperModule = ctx.getLoadedModule(ctx.getIdentifier(CXX_SHIM_NAME)); - assert(wrapperModule && - "CxxShim module is required when using members of a base class. " - "Make sure you `import CxxShim`."); - - SmallVector results; - ctx.lookupInModule(wrapperModule, "__swift_interopStaticCast", results); - assert( - results.size() == 1 && - "Did you forget to define a __swift_interopStaticCast helper function?"); - FuncDecl *staticCastFn = cast(results.back()); - - // Now we have to force instantiate this. We can't let the type checker do - // this yet because it can't infer the "To" type. - auto subst = - SubstitutionMap::get(staticCastFn->getGenericSignature(), {derived, base}, - LookUpConformanceInModule()); - auto functionTemplate = const_cast( - cast(staticCastFn->getClangDecl())); - auto spec = ctx.getClangModuleLoader()->instantiateCXXFunctionTemplate( - ctx, functionTemplate, subst); - auto specializedStaticCastFn = - cast(ctx.getClangModuleLoader()->importDeclDirectly(spec)); - - auto staticCastRefExpr = new (ctx) - DeclRefExpr(ConcreteDeclRef(specializedStaticCastFn), DeclNameLoc(), - /*implicit*/ true); - staticCastRefExpr->setType(specializedStaticCastFn->getInterfaceType()); - - return staticCastRefExpr; -} - -// Create the following expressions: -// %0 = Builtin.addressof(&self) -// %1 = Builtin.reinterpretCast>(%0) -// %2 = __swift_interopStaticCast?>(%1) -// %3 = %2! -// return %3.pointee -static -MemberRefExpr *getSelfInteropStaticCast(FuncDecl *funcDecl, - NominalTypeDecl *baseStruct, - NominalTypeDecl *derivedStruct) { - auto &ctx = funcDecl->getASTContext(); - - auto mutableSelf = [&ctx](FuncDecl *funcDecl) { - auto selfDecl = funcDecl->getImplicitSelfDecl(); - - auto selfRef = - new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/ true); - selfRef->setType(LValueType::get(selfDecl->getInterfaceType())); - - return selfRef; - }(funcDecl); - - auto createCallToBuiltin = [&](Identifier name, ArrayRef substTypes, - Argument arg) { - auto builtinFn = cast(getBuiltinValueDecl(ctx, name)); - auto substMap = - SubstitutionMap::get(builtinFn->getGenericSignature(), substTypes, - LookUpConformanceInModule()); - ConcreteDeclRef builtinFnRef(builtinFn, substMap); - auto builtinFnRefExpr = - new (ctx) DeclRefExpr(builtinFnRef, DeclNameLoc(), /*implicit*/ true); - - auto fnType = builtinFn->getInterfaceType(); - if (auto genericFnType = dyn_cast(fnType.getPointer())) - fnType = genericFnType->substGenericArgs(substMap); - builtinFnRefExpr->setType(fnType); - auto *argList = ArgumentList::createImplicit(ctx, {arg}); - auto callExpr = CallExpr::create(ctx, builtinFnRefExpr, argList, /*implicit*/ true); - callExpr->setThrows(nullptr); - return callExpr; - }; - - auto rawSelfPointer = createCallToBuiltin( - ctx.getIdentifier("addressof"), {derivedStruct->getSelfInterfaceType()}, - Argument::implicitInOut(ctx, mutableSelf)); - rawSelfPointer->setType(ctx.TheRawPointerType); - - auto derivedPtrType = derivedStruct->getSelfInterfaceType()->wrapInPointer( - PTK_UnsafeMutablePointer); - auto selfPointer = - createCallToBuiltin(ctx.getIdentifier("reinterpretCast"), - {ctx.TheRawPointerType, derivedPtrType}, - Argument::unlabeled(rawSelfPointer)); - selfPointer->setType(derivedPtrType); - - auto staticCastRefExpr = getInteropStaticCastDeclRefExpr( - ctx, baseStruct->getClangDecl()->getOwningModule(), - baseStruct->getSelfInterfaceType()->wrapInPointer( - PTK_UnsafeMutablePointer), - derivedStruct->getSelfInterfaceType()->wrapInPointer( - PTK_UnsafeMutablePointer)); - auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfPointer}); - auto casted = CallExpr::createImplicit(ctx, staticCastRefExpr, argList); - // This will be "Optional>" - casted->setType(cast(staticCastRefExpr->getType().getPointer()) - ->getResult()); - casted->setThrows(nullptr); - - SubstitutionMap pointeeSubst = SubstitutionMap::get( - ctx.getUnsafeMutablePointerDecl()->getGenericSignature(), - {baseStruct->getSelfInterfaceType()}, - LookUpConformanceInModule()); - VarDecl *pointeePropertyDecl = - ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer); - auto pointeePropertyRefExpr = new (ctx) MemberRefExpr( - casted, SourceLoc(), - ConcreteDeclRef(pointeePropertyDecl, pointeeSubst), DeclNameLoc(), - /*implicit=*/true); - pointeePropertyRefExpr->setType( - LValueType::get(baseStruct->getSelfInterfaceType())); - - return pointeePropertyRefExpr; -} - -// Find the base C++ method called by the base function we want to synthesize -// the derived thunk for. -// The base C++ method is either the original C++ method that corresponds -// to the imported base member, or it's the synthesized C++ method thunk -// used in another synthesized derived thunk that acts as a base member here. -const clang::CXXMethodDecl *getCalledBaseCxxMethod(FuncDecl *baseMember) { - if (baseMember->getClangDecl()) - return dyn_cast(baseMember->getClangDecl()); - // Another synthesized derived thunk is used as a base member here, - // so extract its synthesized C++ method. - auto body = baseMember->getBody(); - if (body->getElements().empty()) - return nullptr; - ReturnStmt *returnStmt = dyn_cast_or_null( - body->getElements().front().dyn_cast()); - if (!returnStmt) - return nullptr; - Expr *returnExpr = returnStmt->getResult(); - // Look through a potential 'reinterpretCast' that can be used - // to cast UnsafeMutablePointer to UnsafePointer in the synthesized - // Swift body for `.pointee`. - if (auto *ce = dyn_cast(returnExpr)) { - if (auto *v = ce->getCalledValue()) { - if (v->getModuleContext() == - baseMember->getASTContext().TheBuiltinModule && - v->getBaseName().userFacingName() == "reinterpretCast") { - returnExpr = ce->getArgs()->get(0).getExpr(); - } - } - } - // A member ref expr for `.pointee` access can be wrapping a call - // when looking through the synthesized Swift body for `.pointee` - // accessor. - if (MemberRefExpr *mre = dyn_cast(returnExpr)) - returnExpr = mre->getBase(); - auto *callExpr = dyn_cast(returnExpr); - if (!callExpr) - return nullptr; - auto *cv = callExpr->getCalledValue(); - if (!cv) - return nullptr; - if (!cv->getClangDecl()) - return nullptr; - return dyn_cast(cv->getClangDecl()); -} - -// Construct a Swift method that represents the synthesized C++ method -// that invokes the base C++ method. -static FuncDecl *synthesizeBaseFunctionDeclCall(ClangImporter &impl, - ASTContext &ctx, - NominalTypeDecl *derivedStruct, - NominalTypeDecl *baseStruct, - FuncDecl *baseMember) { - auto *cxxMethod = getCalledBaseCxxMethod(baseMember); - if (!cxxMethod) - return nullptr; - auto *newClangMethod = - SwiftDeclSynthesizer(&impl).synthesizeCXXForwardingMethod( - cast(derivedStruct->getClangDecl()), - cast(baseStruct->getClangDecl()), cxxMethod, - ForwardingMethodKind::Base); - if (!newClangMethod) - return nullptr; - return cast_or_null( - ctx.getClangModuleLoader()->importDeclDirectly(newClangMethod)); -} - -// Generates the body of a derived method, that invokes the base -// method. -// The method's body takes the following form: -// return self.__synthesizedBaseCall_fn(args...) -static std::pair -synthesizeBaseClassMethodBody(AbstractFunctionDecl *afd, void *context) { - - ASTContext &ctx = afd->getASTContext(); - - auto funcDecl = cast(afd); - auto derivedStruct = - cast(funcDecl->getDeclContext()->getAsDecl()); - auto baseMember = static_cast(context); - auto baseStruct = - cast(baseMember->getDeclContext()->getAsDecl()); - - auto forwardedFunc = synthesizeBaseFunctionDeclCall( - *static_cast(ctx.getClangModuleLoader()), ctx, - derivedStruct, baseStruct, baseMember); - if (!forwardedFunc) { - ctx.Diags.diagnose(SourceLoc(), diag::failed_base_method_call_synthesis, - funcDecl, baseStruct); - auto body = BraceStmt::create(ctx, SourceLoc(), {}, SourceLoc(), - /*implicit=*/true); - return {body, /*isTypeChecked=*/true}; - } - - SmallVector forwardingParams; - for (auto param : *funcDecl->getParameters()) { - auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(), - /*Implicit=*/true); - paramRefExpr->setType(param->getTypeInContext()); - forwardingParams.push_back(paramRefExpr); - } - - Argument selfArg = [&]() { - auto *selfDecl = funcDecl->getImplicitSelfDecl(); - auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), - /*implicit*/ true); - if (funcDecl->isMutating()) { - selfExpr->setType(LValueType::get(selfDecl->getInterfaceType())); - return Argument::implicitInOut(ctx, selfExpr); - } - selfExpr->setType(selfDecl->getTypeInContext()); - return Argument::unlabeled(selfExpr); - }(); - - auto *baseMemberExpr = - new (ctx) DeclRefExpr(ConcreteDeclRef(forwardedFunc), DeclNameLoc(), - /*Implicit=*/true); - baseMemberExpr->setType(forwardedFunc->getInterfaceType()); - - auto baseMemberDotCallExpr = - DotSyntaxCallExpr::create(ctx, baseMemberExpr, SourceLoc(), selfArg); - baseMemberDotCallExpr->setType(baseMember->getMethodInterfaceType()); - baseMemberDotCallExpr->setThrows(nullptr); - - auto *argList = ArgumentList::forImplicitUnlabeled(ctx, forwardingParams); - auto *baseMemberCallExpr = CallExpr::createImplicit( - ctx, baseMemberDotCallExpr, argList); - baseMemberCallExpr->setType(baseMember->getResultInterfaceType()); - baseMemberCallExpr->setThrows(nullptr); - - auto *returnStmt = ReturnStmt::createImplicit(ctx, baseMemberCallExpr); - - auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), - /*implicit=*/true); - return {body, /*isTypeChecked=*/true}; -} - -// How should the synthesized C++ method that returns the field of interest -// from the base class should return the value - by value, or by reference. -enum ReferenceReturnTypeBehaviorForBaseAccessorSynthesis { - ReturnByValue, - ReturnByReference, - ReturnByMutableReference -}; - -// Synthesize a C++ method that returns the field of interest from the base -// class. This lets Clang take care of the cast from the derived class -// to the base class while the field is accessed. -static clang::CXXMethodDecl *synthesizeCxxBaseGetterAccessorMethod( - ClangImporter &impl, const clang::CXXRecordDecl *derivedClass, - const clang::CXXRecordDecl *baseClass, const clang::FieldDecl *field, - ValueDecl *retainOperationFn, - ReferenceReturnTypeBehaviorForBaseAccessorSynthesis behavior) { - auto &clangCtx = impl.getClangASTContext(); - auto &clangSema = impl.getClangSema(); - - // Create a new method in the derived class that calls the base method. - auto name = field->getDeclName(); - if (name.isIdentifier()) { - std::string newName; - llvm::raw_string_ostream os(newName); - os << (behavior == ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: - ReturnByMutableReference - ? "__synthesizedBaseSetterAccessor_" - : "__synthesizedBaseGetterAccessor_") - << name.getAsIdentifierInfo()->getName(); - name = clang::DeclarationName( - &impl.getClangPreprocessor().getIdentifierTable().get(os.str())); - } - auto returnType = field->getType(); - if (returnType->isReferenceType()) - returnType = returnType->getPointeeType(); - auto valueReturnType = returnType; - if (behavior != - ReferenceReturnTypeBehaviorForBaseAccessorSynthesis::ReturnByValue) { - returnType = clangCtx.getRValueReferenceType( - behavior == ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: - ReturnByReference - ? returnType.withConst() - : returnType); - } - clang::FunctionProtoType::ExtProtoInfo info; - if (behavior != ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: - ReturnByMutableReference) - info.TypeQuals.addConst(); - info.ExceptionSpec.Type = clang::EST_NoThrow; - auto ftype = clangCtx.getFunctionType(returnType, {}, info); - auto newMethod = clang::CXXMethodDecl::Create( - clangCtx, const_cast(derivedClass), - field->getSourceRange().getBegin(), - clang::DeclarationNameInfo(name, clang::SourceLocation()), ftype, - clangCtx.getTrivialTypeSourceInfo(ftype), clang::SC_None, - /*UsesFPIntrin=*/false, /*isInline=*/true, - clang::ConstexprSpecKind::Unspecified, field->getSourceRange().getEnd()); - newMethod->setImplicit(); - newMethod->setImplicitlyInline(); - newMethod->setAccess(clang::AccessSpecifier::AS_public); - if (retainOperationFn) { - // Return an FRT field at +1. - newMethod->addAttr(clang::CFReturnsRetainedAttr::CreateImplicit(clangCtx)); - } - - // Create a new Clang diagnostic pool to capture any diagnostics - // emitted during the construction of the method. - clang::sema::DelayedDiagnosticPool diagPool{ - clangSema.DelayedDiagnostics.getCurrentPool()}; - auto diagState = clangSema.DelayedDiagnostics.push(diagPool); - - // Returns the expression that accesses the base field from derived type. - auto createFieldAccess = [&]() -> clang::Expr * { - auto *thisExpr = clang::CXXThisExpr::Create( - clangCtx, clang::SourceLocation(), newMethod->getThisType(), - /*IsImplicit=*/false); - clang::QualType baseClassPtr = clangCtx.getRecordType(baseClass); - baseClassPtr.addConst(); - baseClassPtr = clangCtx.getPointerType(baseClassPtr); - - clang::CastKind Kind; - clang::CXXCastPath Path; - clangSema.CheckPointerConversion(thisExpr, baseClassPtr, Kind, Path, - /*IgnoreBaseAccess=*/false, - /*Diagnose=*/true); - auto conv = clangSema.ImpCastExprToType(thisExpr, baseClassPtr, Kind, - clang::VK_PRValue, &Path); - if (!conv.isUsable()) - return nullptr; - auto memberExpr = clangSema.BuildMemberExpr( - conv.get(), /*isArrow=*/true, clang::SourceLocation(), - clang::NestedNameSpecifierLoc(), clang::SourceLocation(), - const_cast(field), - clang::DeclAccessPair::make(const_cast(field), - clang::AS_public), - /*HadMultipleCandidates=*/false, - clang::DeclarationNameInfo(field->getDeclName(), - clang::SourceLocation()), - valueReturnType, clang::VK_LValue, clang::OK_Ordinary); - auto returnCast = clangSema.ImpCastExprToType(memberExpr, valueReturnType, - clang::CK_LValueToRValue, - clang::VK_PRValue); - if (!returnCast.isUsable()) - return nullptr; - return returnCast.get(); - }; - - llvm::SmallVector body; - if (retainOperationFn) { - // Check if the returned value needs to be retained. This might occur if the - // field getter is returning a shared reference type using, as it needs to - // perform the retain to match the expected @owned convention. - auto *retainClangFn = - dyn_cast(retainOperationFn->getClangDecl()); - if (!retainClangFn) { - return nullptr; - } - auto *fnRef = new (clangCtx) clang::DeclRefExpr( - clangCtx, const_cast(retainClangFn), false, - retainClangFn->getType(), clang::ExprValueKind::VK_LValue, - clang::SourceLocation()); - auto fieldExpr = createFieldAccess(); - if (!fieldExpr) - return nullptr; - auto retainCall = clangSema.BuildResolvedCallExpr( - fnRef, const_cast(retainClangFn), - clang::SourceLocation(), {fieldExpr}, clang::SourceLocation()); - if (!retainCall.isUsable()) - return nullptr; - body.push_back(retainCall.get()); - } - - // Construct the method's body. - auto fieldExpr = createFieldAccess(); - if (!fieldExpr) - return nullptr; - auto returnStmt = clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(), - fieldExpr, nullptr); - body.push_back(returnStmt); - - // Check if there were any Clang errors during the construction - // of the method body. - clangSema.DelayedDiagnostics.popWithoutEmitting(diagState); - if (!diagPool.empty()) - return nullptr; - newMethod->setBody(body.size() > 1 - ? clang::CompoundStmt::Create( - clangCtx, body, clang::FPOptionsOverride(), - clang::SourceLocation(), clang::SourceLocation()) - : body[0]); - return newMethod; -} - -// Generates the body of a derived method, that invokes the base -// field getter or the base subscript. -// The method's body takes the following form: -// return self.__synthesizedBaseCall_fn(args...) -static std::pair -synthesizeBaseClassFieldGetterOrAddressGetterBody(AbstractFunctionDecl *afd, - void *context, - AccessorKind kind) { - assert(kind == AccessorKind::Get || kind == AccessorKind::Address || - kind == AccessorKind::MutableAddress); - ASTContext &ctx = afd->getASTContext(); - - AccessorDecl *getterDecl = cast(afd); - AbstractStorageDecl *baseClassVar = static_cast(context); - NominalTypeDecl *baseStruct = - cast(baseClassVar->getDeclContext()->getAsDecl()); - NominalTypeDecl *derivedStruct = - cast(getterDecl->getDeclContext()->getAsDecl()); - - const clang::Decl *baseClangDecl; - if (baseClassVar->getClangDecl()) - baseClangDecl = baseClassVar->getClangDecl(); - else - baseClangDecl = getCalledBaseCxxMethod(baseClassVar->getAccessor(kind)); - - clang::CXXMethodDecl *baseGetterCxxMethod = nullptr; - if (auto *md = dyn_cast_or_null(baseClangDecl)) { - // Subscript operator, or `.pointee` wrapper is represented through a - // generated C++ method call that calls the base operator. - baseGetterCxxMethod = - SwiftDeclSynthesizer( - static_cast(ctx.getClangModuleLoader())) - .synthesizeCXXForwardingMethod( - cast(derivedStruct->getClangDecl()), - cast(baseStruct->getClangDecl()), md, - ForwardingMethodKind::Base, - getterDecl->getResultInterfaceType()->isForeignReferenceType() - ? ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: - RemoveReferenceIfPointer - : (kind != AccessorKind::Get - ? ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: - KeepReference - : ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: - RemoveReference), - /*forceConstQualifier=*/kind != AccessorKind::MutableAddress); - } else if (auto *fd = dyn_cast_or_null(baseClangDecl)) { - ValueDecl *retainOperationFn = nullptr; - // Check if this field getter is returning a retainable FRT. - if (getterDecl->getResultInterfaceType()->isForeignReferenceType()) { - auto retainOperation = evaluateOrDefault( - ctx.evaluator, - CustomRefCountingOperation({getterDecl->getResultInterfaceType() - ->lookThroughAllOptionalTypes() - ->getClassOrBoundGenericClass(), - CustomRefCountingOperationKind::retain}), - {}); - if (retainOperation.kind == - CustomRefCountingOperationResult::foundOperation) { - retainOperationFn = retainOperation.operation; - } - } - // Field getter is represented through a generated - // C++ method call that returns the value of the base field. - baseGetterCxxMethod = synthesizeCxxBaseGetterAccessorMethod( - *static_cast(ctx.getClangModuleLoader()), - cast(derivedStruct->getClangDecl()), - cast(baseStruct->getClangDecl()), fd, - retainOperationFn, - kind == AccessorKind::Get - ? ReferenceReturnTypeBehaviorForBaseAccessorSynthesis::ReturnByValue - : (kind == AccessorKind::Address - ? ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: - ReturnByReference - : ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: - ReturnByMutableReference)); - } - - if (!baseGetterCxxMethod) { - ctx.Diags.diagnose(SourceLoc(), diag::failed_base_method_call_synthesis, - getterDecl, baseStruct); - auto body = BraceStmt::create(ctx, SourceLoc(), {}, SourceLoc(), - /*implicit=*/true); - return {body, true}; - } - auto *baseGetterMethod = cast( - ctx.getClangModuleLoader()->importDeclDirectly(baseGetterCxxMethod)); - - Argument selfArg = [&]() { - auto selfDecl = getterDecl->getImplicitSelfDecl(); - auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), - /*implicit*/ true); - if (kind == AccessorKind::MutableAddress) { - selfExpr->setType(LValueType::get(selfDecl->getInterfaceType())); - return Argument::implicitInOut(ctx, selfExpr); - } - selfExpr->setType(selfDecl->getTypeInContext()); - return Argument::unlabeled(selfExpr); - }(); - - auto *baseMemberExpr = - new (ctx) DeclRefExpr(ConcreteDeclRef(baseGetterMethod), DeclNameLoc(), - /*Implicit=*/true); - baseMemberExpr->setType(baseGetterMethod->getInterfaceType()); - - auto baseMemberDotCallExpr = - DotSyntaxCallExpr::create(ctx, baseMemberExpr, SourceLoc(), selfArg); - baseMemberDotCallExpr->setType(baseGetterMethod->getMethodInterfaceType()); - baseMemberDotCallExpr->setThrows(nullptr); - - ArgumentList *argumentList; - if (isa(baseClassVar)) { - auto paramDecl = getterDecl->getParameters()->get(0); - auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl, DeclNameLoc(), - /*Implicit=*/true); - paramRefExpr->setType(paramDecl->getTypeInContext()); - argumentList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr}); - } else { - argumentList = ArgumentList::forImplicitUnlabeled(ctx, {}); - } - - auto *baseMemberCallExpr = - CallExpr::createImplicit(ctx, baseMemberDotCallExpr, argumentList); - Type resultType = baseGetterMethod->getResultInterfaceType(); - baseMemberCallExpr->setType(resultType); - baseMemberCallExpr->setThrows(nullptr); - - Expr *returnExpr = baseMemberCallExpr; - // Cast an 'address' result from a mutable pointer if needed. - if (kind == AccessorKind::Address && - baseGetterMethod->getResultInterfaceType()->isUnsafeMutablePointer()) { - auto finalResultType = getterDecl->getResultInterfaceType(); - returnExpr = SwiftDeclSynthesizer::synthesizeReturnReinterpretCast( - ctx, baseGetterMethod->getResultInterfaceType(), finalResultType, - returnExpr); - } - - auto *returnStmt = ReturnStmt::createImplicit(ctx, returnExpr); - - auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), - /*implicit=*/true); - return {body, /*isTypeChecked=*/true}; -} - -static std::pair -synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) { - return synthesizeBaseClassFieldGetterOrAddressGetterBody(afd, context, - AccessorKind::Get); -} - -static std::pair -synthesizeBaseClassFieldAddressGetterBody(AbstractFunctionDecl *afd, - void *context) { - return synthesizeBaseClassFieldGetterOrAddressGetterBody( - afd, context, AccessorKind::Address); -} - -// For setters we have to pass self as a pointer and then emit an assign: -// %0 = Builtin.addressof(&self) -// %1 = Builtin.reinterpretCast>(%0) -// %2 = __swift_interopStaticCast?>(%1) -// %3 = %2! -// %4 = %3.pointee -// assign newValue to %4 -static std::pair -synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) { - auto setterDecl = cast(afd); - AbstractStorageDecl *baseClassVar = static_cast(context); - ASTContext &ctx = setterDecl->getASTContext(); - - NominalTypeDecl *baseStruct = - cast(baseClassVar->getDeclContext()->getAsDecl()); - NominalTypeDecl *derivedStruct = - cast(setterDecl->getDeclContext()->getAsDecl()); - - auto *pointeePropertyRefExpr = - getSelfInteropStaticCast(setterDecl, baseStruct, derivedStruct); - - Expr *storedRef = nullptr; - if (auto subscript = dyn_cast(baseClassVar)) { - auto paramDecl = setterDecl->getParameters()->get(1); - auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl, - DeclNameLoc(), - /*Implicit=*/ true); - paramRefExpr->setType(paramDecl->getTypeInContext()); - - auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr}); - storedRef = SubscriptExpr::create(ctx, pointeePropertyRefExpr, argList, subscript); - storedRef->setType(LValueType::get(subscript->getElementInterfaceType())); - } else { - // If the base class var has a clang decl, that means it's an access into a - // stored field. Otherwise, we're looking into another base class, so it's a - // another synthesized accessor. - AccessSemantics accessKind = baseClassVar->getClangDecl() - ? AccessSemantics::DirectToStorage - : AccessSemantics::DirectToImplementation; - - storedRef = - new (ctx) MemberRefExpr(pointeePropertyRefExpr, SourceLoc(), baseClassVar, - DeclNameLoc(), /*Implicit=*/true, accessKind); - storedRef->setType(LValueType::get(cast(baseClassVar)->getTypeInContext())); - } - - auto newValueParamRefExpr = - new (ctx) DeclRefExpr(setterDecl->getParameters()->get(0), DeclNameLoc(), - /*Implicit=*/true); - newValueParamRefExpr->setType(setterDecl->getParameters()->get(0)->getTypeInContext()); - - auto assignExpr = - new (ctx) AssignExpr(storedRef, SourceLoc(), newValueParamRefExpr, - /*implicit*/ true); - assignExpr->setType(TupleType::getEmpty(ctx)); - - auto body = BraceStmt::create(ctx, SourceLoc(), {assignExpr}, SourceLoc(), - /*implicit*/ true); - return {body, /*isTypeChecked=*/true}; -} - -static std::pair -synthesizeBaseClassFieldAddressSetterBody(AbstractFunctionDecl *afd, - void *context) { - return synthesizeBaseClassFieldGetterOrAddressGetterBody( - afd, context, AccessorKind::MutableAddress); -} - -static SmallVector -makeBaseClassMemberAccessors(DeclContext *declContext, - AbstractStorageDecl *computedVar, - AbstractStorageDecl *baseClassVar) { - auto &ctx = declContext->getASTContext(); - auto computedType = computedVar->getInterfaceType(); - auto contextTy = declContext->mapTypeIntoEnvironment(computedType); - - // Use 'address' or 'mutableAddress' accessors for non-copyable - // types, unless the base accessor returns it by value. - bool useAddress = contextTy->isNoncopyable() && - (baseClassVar->getReadImpl() == ReadImplKind::Stored || - baseClassVar->getAccessor(AccessorKind::Address)); - - ParameterList *bodyParams = nullptr; - if (auto subscript = dyn_cast(baseClassVar)) { - computedType = computedType->getAs()->getResult(); - - auto idxParam = subscript->getIndices()->get(0); - bodyParams = ParameterList::create(ctx, { idxParam }); - } else { - bodyParams = ParameterList::createEmpty(ctx); - } - - auto getterDecl = AccessorDecl::create( - ctx, - /*FuncLoc=*/SourceLoc(), - /*AccessorKeywordLoc=*/SourceLoc(), - useAddress ? AccessorKind::Address : AccessorKind::Get, computedVar, - /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), bodyParams, - useAddress ? computedType->wrapInPointer(PTK_UnsafePointer) - : computedType, - declContext); - getterDecl->setIsTransparent(true); - getterDecl->copyFormalAccessFrom(computedVar); - getterDecl->setBodySynthesizer(useAddress - ? synthesizeBaseClassFieldAddressGetterBody - : synthesizeBaseClassFieldGetterBody, - baseClassVar); - if (baseClassVar->getWriteImpl() == WriteImplKind::Immutable) - return {getterDecl}; - - auto newValueParam = - new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), - ctx.getIdentifier("newValue"), declContext); - newValueParam->setSpecifier(ParamSpecifier::Default); - newValueParam->setInterfaceType(computedType); - - SmallVector setterParamDecls; - if (!useAddress) - setterParamDecls.push_back(newValueParam); - if (auto subscript = dyn_cast(baseClassVar)) - setterParamDecls.push_back(subscript->getIndices()->get(0)); - ParameterList *setterBodyParams = - ParameterList::create(ctx, setterParamDecls); - - auto setterDecl = AccessorDecl::create( - ctx, - /*FuncLoc=*/SourceLoc(), - /*AccessorKeywordLoc=*/SourceLoc(), - useAddress ? AccessorKind::MutableAddress : AccessorKind::Set, - computedVar, - /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), setterBodyParams, - useAddress ? computedType->wrapInPointer(PTK_UnsafeMutablePointer) - : TupleType::getEmpty(ctx), - declContext); - setterDecl->setIsTransparent(true); - setterDecl->copyFormalAccessFrom(computedVar); - setterDecl->setBodySynthesizer(useAddress - ? synthesizeBaseClassFieldAddressSetterBody - : synthesizeBaseClassFieldSetterBody, - baseClassVar); - setterDecl->setSelfAccessKind(SelfAccessKind::Mutating); - - return {getterDecl, setterDecl}; -} - -// Clone attributes that have been imported from Clang. -void cloneImportedAttributes(ValueDecl *fromDecl, ValueDecl* toDecl) { - ASTContext &context = fromDecl->getASTContext(); - for (auto attr : fromDecl->getAttrs()) { - switch (attr->getKind()) { - case DeclAttrKind::Available: { - toDecl->addAttribute(cast(attr)->clone(context, true)); - break; - } - case DeclAttrKind::Custom: { - CustomAttr *cAttr = cast(attr); - toDecl->addAttribute( - CustomAttr::create(context, SourceLoc(), cAttr->getTypeExpr(), - /*owner*/ toDecl, cAttr->getInitContext(), - cAttr->getArgs(), /*implicit*/ true)); - break; - } - case DeclAttrKind::DiscardableResult: { - toDecl->addAttribute(new (context) DiscardableResultAttr(true)); - break; - } - case DeclAttrKind::Effects: { - toDecl->addAttribute(cast(attr)->clone(context)); - break; - } - case DeclAttrKind::Final: { - toDecl->addAttribute(new (context) FinalAttr(true)); - break; - } - case DeclAttrKind::Transparent: { - toDecl->addAttribute(new (context) TransparentAttr(true)); - break; - } - case DeclAttrKind::WarnUnqualifiedAccess: { - toDecl->addAttribute(new (context) WarnUnqualifiedAccessAttr(true)); - break; - } - default: - break; - } - } -} - -static ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext, - ClangInheritanceInfo inheritance) { - AccessLevel access = inheritance.accessForBaseDecl(decl); - ASTContext &context = decl->getASTContext(); - - if (auto fn = dyn_cast(decl)) { - // TODO: function templates are specialized during type checking so to - // support these we need to tell Swift to type check the synthesized bodies. - // TODO: we also currently don't support static functions. That shouldn't be - // too hard. - if (fn->isStatic() || - isa_and_nonnull(fn->getClangDecl())) - return nullptr; - if (auto cxxMethod = - dyn_cast_or_null(fn->getClangDecl())) { - // FIXME: if this function has rvalue this, we won't be able to synthesize - // the accessor correctly (https://github.com/apple/swift/issues/69745). - if (cxxMethod->getRefQualifier() == clang::RefQualifierKind::RQ_RValue) - return nullptr; - } - - auto out = FuncDecl::createImplicit( - context, fn->getStaticSpelling(), fn->getName(), - fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(), - fn->getThrownInterfaceType(), - fn->getGenericParams(), fn->getParameters(), - fn->getResultInterfaceType(), newContext); - cloneImportedAttributes(decl, out); - out->setAccess(access); - inheritance.setUnavailableIfNecessary(decl, out); - out->setBodySynthesizer(synthesizeBaseClassMethodBody, fn); - out->setSelfAccessKind(fn->getSelfAccessKind()); - return out; - } - - if (auto subscript = dyn_cast(decl)) { - auto contextTy = - newContext->mapTypeIntoEnvironment(subscript->getElementInterfaceType()); - // Subscripts that return non-copyable types are not yet supported. - // See: https://github.com/apple/swift/issues/70047. - if (contextTy->isNoncopyable()) - return nullptr; - auto out = SubscriptDecl::create( - subscript->getASTContext(), subscript->getName(), subscript->getStaticLoc(), - subscript->getStaticSpelling(), subscript->getSubscriptLoc(), - subscript->getIndices(), subscript->getNameLoc(), subscript->getElementInterfaceType(), - newContext, subscript->getGenericParams()); - out->setAccess(access); - inheritance.setUnavailableIfNecessary(decl, out); - out->setAccessors(SourceLoc(), - makeBaseClassMemberAccessors(newContext, out, subscript), - SourceLoc()); - out->setImplInfo(subscript->getImplInfo()); - return out; - } - - if (auto var = dyn_cast(decl)) { - auto oldContext = var->getDeclContext(); - auto oldTypeDecl = oldContext->getSelfNominalTypeDecl(); - // FIXME: this is a workaround for rdar://128013193 - if (oldTypeDecl->getAttrs().hasAttribute() && - context.LangOpts.CxxInteropUseOpaquePointerForMoveOnly) - return nullptr; - - auto rawMemory = allocateMemoryForDecl(var->getASTContext(), - sizeof(VarDecl), false); - auto out = - new (rawMemory) VarDecl(var->isStatic(), var->getIntroducer(), - var->getLoc(), var->getName(), newContext); - out->setInterfaceType(var->getInterfaceType()); - out->setIsObjC(var->isObjC()); - out->setIsDynamic(var->isDynamic()); - out->setAccess(access); - inheritance.setUnavailableIfNecessary(decl, out); - out->getASTContext().evaluator.cacheOutput(HasStorageRequest{out}, false); - auto accessors = makeBaseClassMemberAccessors(newContext, out, var); - out->setAccessors(SourceLoc(), accessors, SourceLoc()); - auto isMutable = var->getWriteImpl() == WriteImplKind::Immutable - ? StorageIsNotMutable : StorageIsMutable; - out->setImplInfo( - accessors[0]->getAccessorKind() == AccessorKind::Address - ? (accessors.size() > 1 - ? StorageImplInfo(ReadImplKind::Address, - WriteImplKind::MutableAddress, - ReadWriteImplKind::MutableAddress) - : StorageImplInfo(ReadImplKind::Address)) - : StorageImplInfo::getComputed(isMutable)); - out->setIsSetterMutating(true); - return out; - } - - if (auto typeAlias = dyn_cast(decl)) { - auto rawMemory = allocateMemoryForDecl( - typeAlias->getASTContext(), sizeof(TypeAliasDecl), false); - auto out = new (rawMemory) - TypeAliasDecl(typeAlias->getStartLoc(), typeAlias->getEqualLoc(), - typeAlias->getName(), typeAlias->getNameLoc(), - typeAlias->getGenericParams(), newContext); - out->setUnderlyingType(typeAlias->getUnderlyingType()); - out->setAccess(access); - inheritance.setUnavailableIfNecessary(decl, out); - return out; - } - - if (auto typeDecl = dyn_cast(decl)) { - auto rawMemory = allocateMemoryForDecl( - typeDecl->getASTContext(), sizeof(TypeAliasDecl), false); - auto out = new (rawMemory) TypeAliasDecl( - typeDecl->getLoc(), typeDecl->getLoc(), typeDecl->getName(), - typeDecl->getLoc(), nullptr, newContext); - out->setUnderlyingType(typeDecl->getDeclaredInterfaceType()); - out->setAccess(access); - inheritance.setUnavailableIfNecessary(decl, out); - return out; - } - - return nullptr; -} - -TinyPtrVector ClangRecordMemberLookup::evaluate( - Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const { - NominalTypeDecl *recordDecl = desc.recordDecl; - NominalTypeDecl *inheritingDecl = desc.inheritingDecl; - DeclName name = desc.name; - ClangInheritanceInfo inheritance = desc.inheritance; - - auto &ctx = recordDecl->getASTContext(); - - // Whether to skip non-public members. Feature::ImportNonPublicCxxMembers says - // to import all non-public members by default; if that is disabled, we only - // import non-public members annotated with SWIFT_PRIVATE_FILEID (since those - // are the only classes that need non-public members.) - auto *cxxRecordDecl = - dyn_cast(inheritingDecl->getClangDecl()); - auto skipIfNonPublic = - !ctx.LangOpts.hasFeature(Feature::ImportNonPublicCxxMembers) && - cxxRecordDecl && importer::getPrivateFileIDAttrs(cxxRecordDecl).empty(); - - auto directResults = evaluateOrDefault( - ctx.evaluator, - ClangDirectLookupRequest({recordDecl, recordDecl->getClangDecl(), name}), - {}); - - // The set of declarations we found. - TinyPtrVector result; - CollectLookupResults collector(name, result); - - // Find the results that are actually a member of "recordDecl". - ClangModuleLoader *clangModuleLoader = ctx.getClangModuleLoader(); - for (auto foundEntry : directResults) { - auto found = cast(foundEntry); - if (dyn_cast(found->getDeclContext()) != - recordDecl->getClangDecl()) - continue; - - // We should not import 'found' if the following are all true: - // - // - Feature::ImportNonPublicCxxMembers is not enabled - // - 'found' is not a member of a SWIFT_PRIVATE_FILEID-annotated class - // - 'found' is a non-public member. - // - 'found' is not a non-inherited FieldDecl; we must import private - // fields because they may affect implicit conformances that iterate - // through all of a struct's fields, e.g., Sendable (#76892). - // - // Note that we can skip inherited FieldDecls because implicit conformances - // handle those separately. - // - // The first two conditions are captured by skipIfNonPublic. The next two - // are conveyed by the following: - auto nonPublic = found->getAccess() == clang::AS_private || - found->getAccess() == clang::AS_protected; - auto noninheritedField = !inheritance && isa(found); - if (skipIfNonPublic && nonPublic && !noninheritedField) - continue; - - // Don't import constructors on foreign reference types. - if (isa(found) && isa(recordDecl)) - continue; - - auto imported = clangModuleLoader->importDeclDirectly(found); - if (!imported) - continue; - - // If this member is found due to inheritance, clone it from the base class - // by synthesizing getters and setters. - if (inheritance) { - imported = clangModuleLoader->importBaseMemberDecl( - cast(imported), inheritingDecl, inheritance); - if (!imported) - continue; - } - - collector.add(cast(imported)); - } - - if (inheritance) { - // For inherited members, add members that are synthesized eagerly, such as - // subscripts. This is not necessary for non-inherited members because those - // should already be in the lookup table. - for (auto member : - cast(recordDecl)->getCurrentMembersWithoutLoading()) { - auto namedMember = dyn_cast(member); - if (!namedMember || !namedMember->hasName() || - namedMember->getName().getBaseName() != name || - clangModuleLoader->getOriginalForClonedMember(namedMember)) - continue; - - auto *imported = clangModuleLoader->importBaseMemberDecl( - namedMember, inheritingDecl, inheritance); - if (!imported) - continue; - - collector.add(imported); - } - } - - // If this is a C++ record, look through any base classes. - const clang::CXXRecordDecl *cxxRecord; - if ((cxxRecord = dyn_cast(recordDecl->getClangDecl())) && - cxxRecord->isCompleteDefinition()) { - // Capture the arity of already found members in the - // current record, to avoid adding ambiguous members - // from base classes. - llvm::SmallSet foundMethodNames; - for (const auto *valueDecl : result) - foundMethodNames.insert(valueDecl->getName()); - - for (auto base : cxxRecord->bases()) { - if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public) - continue; - - clang::QualType baseType = base.getType(); - if (auto spectType = dyn_cast(baseType)) - baseType = spectType->desugar(); - if (!isa(baseType.getCanonicalType())) - continue; - - auto *baseRecord = baseType->getAs()->getDecl(); - - if (isSymbolicCircularBase(cxxRecord, baseRecord)) - // Skip circular bases to avoid unbounded recursion - continue; - - 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(import)->getName() == name) - continue; - - auto baseInheritance = ClangInheritanceInfo(inheritance, base); - - // Add Clang members that are imported lazily. - auto baseResults = evaluateOrDefault( - ctx.evaluator, - ClangRecordMemberLookup({cast(import), name, - inheritingDecl, baseInheritance}), - {}); - - for (auto foundInBase : baseResults) { - // Do not add duplicate entry with the same DeclName, - // as that would cause an ambiguous lookup. - if (foundMethodNames.count(foundInBase->getName())) - continue; - - collector.add(foundInBase); - } - } - } - } - - return result; -} - IterableDeclContext *IterableDeclContext::getImplementationContext() { if (auto implDecl = getDecl()->getObjCImplementationDecl()) if (auto implExt = dyn_cast(implDecl)) @@ -7882,48 +6807,6 @@ Decl *ClangImporter::importDeclDirectly(const clang::NamedDecl *decl) { return Impl.importDecl(decl, Impl.CurrentVersion); } -ValueDecl *ClangImporter::Implementation::importBaseMemberDecl( - ValueDecl *decl, DeclContext *newContext, - ClangInheritanceInfo inheritance) { - - // 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 key = {decl, newContext}; - auto known = clonedBaseMembers.find(key); - if (known == clonedBaseMembers.end()) { - ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance); - handleAmbiguousSwiftName(cloned); - known = clonedBaseMembers.insert({key, cloned}).first; - clonedMembers.insert(std::make_pair(cloned, decl)); - } - - return known->second; -} - -ValueDecl *ClangImporter::Implementation::getOriginalForClonedMember( - const ValueDecl *decl) { - // If this is a cloned decl, we don't want to reclone it - // Otherwise, we may end up with multiple copies of the same method - if (!decl->hasClangNode()) { - // Skip decls with a clang node as those will never be a clone - auto result = clonedMembers.find(decl); - if (result != clonedMembers.end()) - return result->getSecond(); - } - - return nullptr; -} - -size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity( - const ValueDecl *valueDecl) { - if (auto *func = dyn_cast(valueDecl)) { - if (auto *params = func->getParameters()) { - return params->size(); - } - } - return 0; -} - ValueDecl * ClangImporter::importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext, ClangInheritanceInfo inheritance) { diff --git a/lib/ClangImporter/ClangLookup.cpp b/lib/ClangImporter/ClangLookup.cpp index c2ccdc320ff16..05c805c712087 100644 --- a/lib/ClangImporter/ClangLookup.cpp +++ b/lib/ClangImporter/ClangLookup.cpp @@ -15,12 +15,14 @@ //===----------------------------------------------------------------------===// #include "ImporterImpl.h" +#include "SwiftDeclSynthesizer.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Builtins.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/Evaluator.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Module.h" @@ -33,21 +35,42 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/SourceLoc.h" +#include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterRequests.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Parse/ParseVersion.h" +#include "swift/Strings.h" #include "swift/Subsystems.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" +#include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Module.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Frontend/Utils.h" #include "clang/Index/IndexingAction.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/Sema.h" #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CAS/CASReference.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Memory.h" +#include +#include #include using namespace swift; @@ -210,3 +233,1091 @@ TinyPtrVector CXXNamespaceMemberLookup::evaluate( return result; } + +// Just create a specialized function decl for "__swift_interopStaticCast" +// using the types base and derived. +static DeclRefExpr * +getInteropStaticCastDeclRefExpr(ASTContext &ctx, + const clang::Module *owningModule, Type base, + Type derived) { + if (base->isForeignReferenceType() && derived->isForeignReferenceType()) { + base = base->wrapInPointer(PTK_UnsafePointer); + derived = derived->wrapInPointer(PTK_UnsafePointer); + } + + // Lookup our static cast helper function in the C++ shim module. + auto wrapperModule = ctx.getLoadedModule(ctx.getIdentifier(CXX_SHIM_NAME)); + assert(wrapperModule && + "CxxShim module is required when using members of a base class. " + "Make sure you `import CxxShim`."); + + SmallVector results; + ctx.lookupInModule(wrapperModule, "__swift_interopStaticCast", results); + assert( + results.size() == 1 && + "Did you forget to define a __swift_interopStaticCast helper function?"); + FuncDecl *staticCastFn = cast(results.back()); + + // Now we have to force instantiate this. We can't let the type checker do + // this yet because it can't infer the "To" type. + auto subst = + SubstitutionMap::get(staticCastFn->getGenericSignature(), {derived, base}, + LookUpConformanceInModule()); + auto functionTemplate = const_cast( + cast(staticCastFn->getClangDecl())); + auto spec = ctx.getClangModuleLoader()->instantiateCXXFunctionTemplate( + ctx, functionTemplate, subst); + auto specializedStaticCastFn = + cast(ctx.getClangModuleLoader()->importDeclDirectly(spec)); + + auto staticCastRefExpr = new (ctx) + DeclRefExpr(ConcreteDeclRef(specializedStaticCastFn), DeclNameLoc(), + /*implicit*/ true); + staticCastRefExpr->setType(specializedStaticCastFn->getInterfaceType()); + + return staticCastRefExpr; +} + +// Create the following expressions: +// %0 = Builtin.addressof(&self) +// %1 = Builtin.reinterpretCast>(%0) +// %2 = __swift_interopStaticCast?>(%1) +// %3 = %2! +// return %3.pointee +static MemberRefExpr *getSelfInteropStaticCast(FuncDecl *funcDecl, + NominalTypeDecl *baseStruct, + NominalTypeDecl *derivedStruct) { + auto &ctx = funcDecl->getASTContext(); + + auto mutableSelf = [&ctx](FuncDecl *funcDecl) { + auto selfDecl = funcDecl->getImplicitSelfDecl(); + + auto selfRef = + new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/ true); + selfRef->setType(LValueType::get(selfDecl->getInterfaceType())); + + return selfRef; + }(funcDecl); + + auto createCallToBuiltin = [&](Identifier name, ArrayRef substTypes, + Argument arg) { + auto builtinFn = cast(getBuiltinValueDecl(ctx, name)); + auto substMap = + SubstitutionMap::get(builtinFn->getGenericSignature(), substTypes, + LookUpConformanceInModule()); + ConcreteDeclRef builtinFnRef(builtinFn, substMap); + auto builtinFnRefExpr = + new (ctx) DeclRefExpr(builtinFnRef, DeclNameLoc(), /*implicit*/ true); + + auto fnType = builtinFn->getInterfaceType(); + if (auto genericFnType = dyn_cast(fnType.getPointer())) + fnType = genericFnType->substGenericArgs(substMap); + builtinFnRefExpr->setType(fnType); + auto *argList = ArgumentList::createImplicit(ctx, {arg}); + auto callExpr = + CallExpr::create(ctx, builtinFnRefExpr, argList, /*implicit*/ true); + callExpr->setThrows(nullptr); + return callExpr; + }; + + auto rawSelfPointer = createCallToBuiltin( + ctx.getIdentifier("addressof"), {derivedStruct->getSelfInterfaceType()}, + Argument::implicitInOut(ctx, mutableSelf)); + rawSelfPointer->setType(ctx.TheRawPointerType); + + auto derivedPtrType = derivedStruct->getSelfInterfaceType()->wrapInPointer( + PTK_UnsafeMutablePointer); + auto selfPointer = + createCallToBuiltin(ctx.getIdentifier("reinterpretCast"), + {ctx.TheRawPointerType, derivedPtrType}, + Argument::unlabeled(rawSelfPointer)); + selfPointer->setType(derivedPtrType); + + auto staticCastRefExpr = getInteropStaticCastDeclRefExpr( + ctx, baseStruct->getClangDecl()->getOwningModule(), + baseStruct->getSelfInterfaceType()->wrapInPointer( + PTK_UnsafeMutablePointer), + derivedStruct->getSelfInterfaceType()->wrapInPointer( + PTK_UnsafeMutablePointer)); + auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfPointer}); + auto casted = CallExpr::createImplicit(ctx, staticCastRefExpr, argList); + // This will be "Optional>" + casted->setType(cast(staticCastRefExpr->getType().getPointer()) + ->getResult()); + casted->setThrows(nullptr); + + SubstitutionMap pointeeSubst = SubstitutionMap::get( + ctx.getUnsafeMutablePointerDecl()->getGenericSignature(), + {baseStruct->getSelfInterfaceType()}, LookUpConformanceInModule()); + VarDecl *pointeePropertyDecl = + ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer); + auto pointeePropertyRefExpr = new (ctx) MemberRefExpr( + casted, SourceLoc(), ConcreteDeclRef(pointeePropertyDecl, pointeeSubst), + DeclNameLoc(), + /*implicit=*/true); + pointeePropertyRefExpr->setType( + LValueType::get(baseStruct->getSelfInterfaceType())); + + return pointeePropertyRefExpr; +} + +// Find the base C++ method called by the base function we want to synthesize +// the derived thunk for. +// The base C++ method is either the original C++ method that corresponds +// to the imported base member, or it's the synthesized C++ method thunk +// used in another synthesized derived thunk that acts as a base member here. +static const clang::CXXMethodDecl * +getCalledBaseCxxMethod(FuncDecl *baseMember) { + if (baseMember->getClangDecl()) + return dyn_cast(baseMember->getClangDecl()); + // Another synthesized derived thunk is used as a base member here, + // so extract its synthesized C++ method. + auto body = baseMember->getBody(); + if (body->getElements().empty()) + return nullptr; + ReturnStmt *returnStmt = dyn_cast_or_null( + body->getElements().front().dyn_cast()); + if (!returnStmt) + return nullptr; + Expr *returnExpr = returnStmt->getResult(); + // Look through a potential 'reinterpretCast' that can be used + // to cast UnsafeMutablePointer to UnsafePointer in the synthesized + // Swift body for `.pointee`. + if (auto *ce = dyn_cast(returnExpr)) { + if (auto *v = ce->getCalledValue()) { + if (v->getModuleContext() == + baseMember->getASTContext().TheBuiltinModule && + v->getBaseName().userFacingName() == "reinterpretCast") { + returnExpr = ce->getArgs()->get(0).getExpr(); + } + } + } + // A member ref expr for `.pointee` access can be wrapping a call + // when looking through the synthesized Swift body for `.pointee` + // accessor. + if (MemberRefExpr *mre = dyn_cast(returnExpr)) + returnExpr = mre->getBase(); + auto *callExpr = dyn_cast(returnExpr); + if (!callExpr) + return nullptr; + auto *cv = callExpr->getCalledValue(); + if (!cv) + return nullptr; + if (!cv->getClangDecl()) + return nullptr; + return dyn_cast(cv->getClangDecl()); +} + +// Construct a Swift method that represents the synthesized C++ method +// that invokes the base C++ method. +static FuncDecl *synthesizeBaseFunctionDeclCall(ClangImporter &impl, + ASTContext &ctx, + NominalTypeDecl *derivedStruct, + NominalTypeDecl *baseStruct, + FuncDecl *baseMember) { + auto *cxxMethod = getCalledBaseCxxMethod(baseMember); + if (!cxxMethod) + return nullptr; + auto *newClangMethod = + SwiftDeclSynthesizer(&impl).synthesizeCXXForwardingMethod( + cast(derivedStruct->getClangDecl()), + cast(baseStruct->getClangDecl()), cxxMethod, + ForwardingMethodKind::Base); + if (!newClangMethod) + return nullptr; + return cast_or_null( + ctx.getClangModuleLoader()->importDeclDirectly(newClangMethod)); +} + +// Generates the body of a derived method, that invokes the base +// method. +// The method's body takes the following form: +// return self.__synthesizedBaseCall_fn(args...) +static std::pair +synthesizeBaseClassMethodBody(AbstractFunctionDecl *afd, void *context) { + + ASTContext &ctx = afd->getASTContext(); + + auto funcDecl = cast(afd); + auto derivedStruct = + cast(funcDecl->getDeclContext()->getAsDecl()); + auto baseMember = static_cast(context); + auto baseStruct = + cast(baseMember->getDeclContext()->getAsDecl()); + + auto forwardedFunc = synthesizeBaseFunctionDeclCall( + *static_cast(ctx.getClangModuleLoader()), ctx, + derivedStruct, baseStruct, baseMember); + if (!forwardedFunc) { + ctx.Diags.diagnose(SourceLoc(), diag::failed_base_method_call_synthesis, + funcDecl, baseStruct); + auto body = BraceStmt::create(ctx, SourceLoc(), {}, SourceLoc(), + /*implicit=*/true); + return {body, /*isTypeChecked=*/true}; + } + + SmallVector forwardingParams; + for (auto param : *funcDecl->getParameters()) { + auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(), + /*Implicit=*/true); + paramRefExpr->setType(param->getTypeInContext()); + forwardingParams.push_back(paramRefExpr); + } + + Argument selfArg = [&]() { + auto *selfDecl = funcDecl->getImplicitSelfDecl(); + auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), + /*implicit*/ true); + if (funcDecl->isMutating()) { + selfExpr->setType(LValueType::get(selfDecl->getInterfaceType())); + return Argument::implicitInOut(ctx, selfExpr); + } + selfExpr->setType(selfDecl->getTypeInContext()); + return Argument::unlabeled(selfExpr); + }(); + + auto *baseMemberExpr = + new (ctx) DeclRefExpr(ConcreteDeclRef(forwardedFunc), DeclNameLoc(), + /*Implicit=*/true); + baseMemberExpr->setType(forwardedFunc->getInterfaceType()); + + auto baseMemberDotCallExpr = + DotSyntaxCallExpr::create(ctx, baseMemberExpr, SourceLoc(), selfArg); + baseMemberDotCallExpr->setType(baseMember->getMethodInterfaceType()); + baseMemberDotCallExpr->setThrows(nullptr); + + auto *argList = ArgumentList::forImplicitUnlabeled(ctx, forwardingParams); + auto *baseMemberCallExpr = + CallExpr::createImplicit(ctx, baseMemberDotCallExpr, argList); + baseMemberCallExpr->setType(baseMember->getResultInterfaceType()); + baseMemberCallExpr->setThrows(nullptr); + + auto *returnStmt = ReturnStmt::createImplicit(ctx, baseMemberCallExpr); + + auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), + /*implicit=*/true); + return {body, /*isTypeChecked=*/true}; +} + +// How should the synthesized C++ method that returns the field of interest +// from the base class should return the value - by value, or by reference. +enum ReferenceReturnTypeBehaviorForBaseAccessorSynthesis { + ReturnByValue, + ReturnByReference, + ReturnByMutableReference +}; + +// Synthesize a C++ method that returns the field of interest from the base +// class. This lets Clang take care of the cast from the derived class +// to the base class while the field is accessed. +static clang::CXXMethodDecl *synthesizeCxxBaseGetterAccessorMethod( + ClangImporter &impl, const clang::CXXRecordDecl *derivedClass, + const clang::CXXRecordDecl *baseClass, const clang::FieldDecl *field, + ValueDecl *retainOperationFn, + ReferenceReturnTypeBehaviorForBaseAccessorSynthesis behavior) { + auto &clangCtx = impl.getClangASTContext(); + auto &clangSema = impl.getClangSema(); + + // Create a new method in the derived class that calls the base method. + auto name = field->getDeclName(); + if (name.isIdentifier()) { + std::string newName; + llvm::raw_string_ostream os(newName); + os << (behavior == ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: + ReturnByMutableReference + ? "__synthesizedBaseSetterAccessor_" + : "__synthesizedBaseGetterAccessor_") + << name.getAsIdentifierInfo()->getName(); + name = clang::DeclarationName( + &impl.getClangPreprocessor().getIdentifierTable().get(os.str())); + } + auto returnType = field->getType(); + if (returnType->isReferenceType()) + returnType = returnType->getPointeeType(); + auto valueReturnType = returnType; + if (behavior != + ReferenceReturnTypeBehaviorForBaseAccessorSynthesis::ReturnByValue) { + returnType = clangCtx.getRValueReferenceType( + behavior == ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: + ReturnByReference + ? returnType.withConst() + : returnType); + } + clang::FunctionProtoType::ExtProtoInfo info; + if (behavior != ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: + ReturnByMutableReference) + info.TypeQuals.addConst(); + info.ExceptionSpec.Type = clang::EST_NoThrow; + auto ftype = clangCtx.getFunctionType(returnType, {}, info); + auto newMethod = clang::CXXMethodDecl::Create( + clangCtx, const_cast(derivedClass), + field->getSourceRange().getBegin(), + clang::DeclarationNameInfo(name, clang::SourceLocation()), ftype, + clangCtx.getTrivialTypeSourceInfo(ftype), clang::SC_None, + /*UsesFPIntrin=*/false, /*isInline=*/true, + clang::ConstexprSpecKind::Unspecified, field->getSourceRange().getEnd()); + newMethod->setImplicit(); + newMethod->setImplicitlyInline(); + newMethod->setAccess(clang::AccessSpecifier::AS_public); + if (retainOperationFn) { + // Return an FRT field at +1. + newMethod->addAttr(clang::CFReturnsRetainedAttr::CreateImplicit(clangCtx)); + } + + // Create a new Clang diagnostic pool to capture any diagnostics + // emitted during the construction of the method. + clang::sema::DelayedDiagnosticPool diagPool{ + clangSema.DelayedDiagnostics.getCurrentPool()}; + auto diagState = clangSema.DelayedDiagnostics.push(diagPool); + + // Returns the expression that accesses the base field from derived type. + auto createFieldAccess = [&]() -> clang::Expr * { + auto *thisExpr = clang::CXXThisExpr::Create( + clangCtx, clang::SourceLocation(), newMethod->getThisType(), + /*IsImplicit=*/false); + clang::QualType baseClassPtr = clangCtx.getRecordType(baseClass); + baseClassPtr.addConst(); + baseClassPtr = clangCtx.getPointerType(baseClassPtr); + + clang::CastKind Kind; + clang::CXXCastPath Path; + clangSema.CheckPointerConversion(thisExpr, baseClassPtr, Kind, Path, + /*IgnoreBaseAccess=*/false, + /*Diagnose=*/true); + auto conv = clangSema.ImpCastExprToType(thisExpr, baseClassPtr, Kind, + clang::VK_PRValue, &Path); + if (!conv.isUsable()) + return nullptr; + auto memberExpr = clangSema.BuildMemberExpr( + conv.get(), /*isArrow=*/true, clang::SourceLocation(), + clang::NestedNameSpecifierLoc(), clang::SourceLocation(), + const_cast(field), + clang::DeclAccessPair::make(const_cast(field), + clang::AS_public), + /*HadMultipleCandidates=*/false, + clang::DeclarationNameInfo(field->getDeclName(), + clang::SourceLocation()), + valueReturnType, clang::VK_LValue, clang::OK_Ordinary); + auto returnCast = clangSema.ImpCastExprToType(memberExpr, valueReturnType, + clang::CK_LValueToRValue, + clang::VK_PRValue); + if (!returnCast.isUsable()) + return nullptr; + return returnCast.get(); + }; + + llvm::SmallVector body; + if (retainOperationFn) { + // Check if the returned value needs to be retained. This might occur if the + // field getter is returning a shared reference type using, as it needs to + // perform the retain to match the expected @owned convention. + auto *retainClangFn = + dyn_cast(retainOperationFn->getClangDecl()); + if (!retainClangFn) { + return nullptr; + } + auto *fnRef = new (clangCtx) clang::DeclRefExpr( + clangCtx, const_cast(retainClangFn), false, + retainClangFn->getType(), clang::ExprValueKind::VK_LValue, + clang::SourceLocation()); + auto fieldExpr = createFieldAccess(); + if (!fieldExpr) + return nullptr; + auto retainCall = clangSema.BuildResolvedCallExpr( + fnRef, const_cast(retainClangFn), + clang::SourceLocation(), {fieldExpr}, clang::SourceLocation()); + if (!retainCall.isUsable()) + return nullptr; + body.push_back(retainCall.get()); + } + + // Construct the method's body. + auto fieldExpr = createFieldAccess(); + if (!fieldExpr) + return nullptr; + auto returnStmt = clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(), + fieldExpr, nullptr); + body.push_back(returnStmt); + + // Check if there were any Clang errors during the construction + // of the method body. + clangSema.DelayedDiagnostics.popWithoutEmitting(diagState); + if (!diagPool.empty()) + return nullptr; + newMethod->setBody(body.size() > 1 + ? clang::CompoundStmt::Create( + clangCtx, body, clang::FPOptionsOverride(), + clang::SourceLocation(), clang::SourceLocation()) + : body[0]); + return newMethod; +} + +// Generates the body of a derived method, that invokes the base +// field getter or the base subscript. +// The method's body takes the following form: +// return self.__synthesizedBaseCall_fn(args...) +static std::pair +synthesizeBaseClassFieldGetterOrAddressGetterBody(AbstractFunctionDecl *afd, + void *context, + AccessorKind kind) { + assert(kind == AccessorKind::Get || kind == AccessorKind::Address || + kind == AccessorKind::MutableAddress); + ASTContext &ctx = afd->getASTContext(); + + AccessorDecl *getterDecl = cast(afd); + AbstractStorageDecl *baseClassVar = + static_cast(context); + NominalTypeDecl *baseStruct = + cast(baseClassVar->getDeclContext()->getAsDecl()); + NominalTypeDecl *derivedStruct = + cast(getterDecl->getDeclContext()->getAsDecl()); + + const clang::Decl *baseClangDecl; + if (baseClassVar->getClangDecl()) + baseClangDecl = baseClassVar->getClangDecl(); + else + baseClangDecl = getCalledBaseCxxMethod(baseClassVar->getAccessor(kind)); + + clang::CXXMethodDecl *baseGetterCxxMethod = nullptr; + if (auto *md = dyn_cast_or_null(baseClangDecl)) { + // Subscript operator, or `.pointee` wrapper is represented through a + // generated C++ method call that calls the base operator. + baseGetterCxxMethod = + SwiftDeclSynthesizer( + static_cast(ctx.getClangModuleLoader())) + .synthesizeCXXForwardingMethod( + cast(derivedStruct->getClangDecl()), + cast(baseStruct->getClangDecl()), md, + ForwardingMethodKind::Base, + getterDecl->getResultInterfaceType()->isForeignReferenceType() + ? ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: + RemoveReferenceIfPointer + : (kind != AccessorKind::Get + ? ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: + KeepReference + : ReferenceReturnTypeBehaviorForBaseMethodSynthesis:: + RemoveReference), + /*forceConstQualifier=*/kind != AccessorKind::MutableAddress); + } else if (auto *fd = dyn_cast_or_null(baseClangDecl)) { + ValueDecl *retainOperationFn = nullptr; + // Check if this field getter is returning a retainable FRT. + if (getterDecl->getResultInterfaceType()->isForeignReferenceType()) { + auto retainOperation = evaluateOrDefault( + ctx.evaluator, + CustomRefCountingOperation({getterDecl->getResultInterfaceType() + ->lookThroughAllOptionalTypes() + ->getClassOrBoundGenericClass(), + CustomRefCountingOperationKind::retain}), + {}); + if (retainOperation.kind == + CustomRefCountingOperationResult::foundOperation) { + retainOperationFn = retainOperation.operation; + } + } + // Field getter is represented through a generated + // C++ method call that returns the value of the base field. + baseGetterCxxMethod = synthesizeCxxBaseGetterAccessorMethod( + *static_cast(ctx.getClangModuleLoader()), + cast(derivedStruct->getClangDecl()), + cast(baseStruct->getClangDecl()), fd, + retainOperationFn, + kind == AccessorKind::Get + ? ReferenceReturnTypeBehaviorForBaseAccessorSynthesis::ReturnByValue + : (kind == AccessorKind::Address + ? ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: + ReturnByReference + : ReferenceReturnTypeBehaviorForBaseAccessorSynthesis:: + ReturnByMutableReference)); + } + + if (!baseGetterCxxMethod) { + ctx.Diags.diagnose(SourceLoc(), diag::failed_base_method_call_synthesis, + getterDecl, baseStruct); + auto body = BraceStmt::create(ctx, SourceLoc(), {}, SourceLoc(), + /*implicit=*/true); + return {body, true}; + } + auto *baseGetterMethod = cast( + ctx.getClangModuleLoader()->importDeclDirectly(baseGetterCxxMethod)); + + Argument selfArg = [&]() { + auto selfDecl = getterDecl->getImplicitSelfDecl(); + auto selfExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), + /*implicit*/ true); + if (kind == AccessorKind::MutableAddress) { + selfExpr->setType(LValueType::get(selfDecl->getInterfaceType())); + return Argument::implicitInOut(ctx, selfExpr); + } + selfExpr->setType(selfDecl->getTypeInContext()); + return Argument::unlabeled(selfExpr); + }(); + + auto *baseMemberExpr = + new (ctx) DeclRefExpr(ConcreteDeclRef(baseGetterMethod), DeclNameLoc(), + /*Implicit=*/true); + baseMemberExpr->setType(baseGetterMethod->getInterfaceType()); + + auto baseMemberDotCallExpr = + DotSyntaxCallExpr::create(ctx, baseMemberExpr, SourceLoc(), selfArg); + baseMemberDotCallExpr->setType(baseGetterMethod->getMethodInterfaceType()); + baseMemberDotCallExpr->setThrows(nullptr); + + ArgumentList *argumentList; + if (isa(baseClassVar)) { + auto paramDecl = getterDecl->getParameters()->get(0); + auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl, DeclNameLoc(), + /*Implicit=*/true); + paramRefExpr->setType(paramDecl->getTypeInContext()); + argumentList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr}); + } else { + argumentList = ArgumentList::forImplicitUnlabeled(ctx, {}); + } + + auto *baseMemberCallExpr = + CallExpr::createImplicit(ctx, baseMemberDotCallExpr, argumentList); + Type resultType = baseGetterMethod->getResultInterfaceType(); + baseMemberCallExpr->setType(resultType); + baseMemberCallExpr->setThrows(nullptr); + + Expr *returnExpr = baseMemberCallExpr; + // Cast an 'address' result from a mutable pointer if needed. + if (kind == AccessorKind::Address && + baseGetterMethod->getResultInterfaceType()->isUnsafeMutablePointer()) { + auto finalResultType = getterDecl->getResultInterfaceType(); + returnExpr = SwiftDeclSynthesizer::synthesizeReturnReinterpretCast( + ctx, baseGetterMethod->getResultInterfaceType(), finalResultType, + returnExpr); + } + + auto *returnStmt = ReturnStmt::createImplicit(ctx, returnExpr); + + auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), + /*implicit=*/true); + return {body, /*isTypeChecked=*/true}; +} + +static std::pair +synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) { + return synthesizeBaseClassFieldGetterOrAddressGetterBody(afd, context, + AccessorKind::Get); +} + +static std::pair +synthesizeBaseClassFieldAddressGetterBody(AbstractFunctionDecl *afd, + void *context) { + return synthesizeBaseClassFieldGetterOrAddressGetterBody( + afd, context, AccessorKind::Address); +} + +// For setters we have to pass self as a pointer and then emit an assign: +// %0 = Builtin.addressof(&self) +// %1 = Builtin.reinterpretCast>(%0) +// %2 = __swift_interopStaticCast?>(%1) +// %3 = %2! +// %4 = %3.pointee +// assign newValue to %4 +static std::pair +synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) { + auto setterDecl = cast(afd); + AbstractStorageDecl *baseClassVar = + static_cast(context); + ASTContext &ctx = setterDecl->getASTContext(); + + NominalTypeDecl *baseStruct = + cast(baseClassVar->getDeclContext()->getAsDecl()); + NominalTypeDecl *derivedStruct = + cast(setterDecl->getDeclContext()->getAsDecl()); + + auto *pointeePropertyRefExpr = + getSelfInteropStaticCast(setterDecl, baseStruct, derivedStruct); + + Expr *storedRef = nullptr; + if (auto subscript = dyn_cast(baseClassVar)) { + auto paramDecl = setterDecl->getParameters()->get(1); + auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl, DeclNameLoc(), + /*Implicit=*/true); + paramRefExpr->setType(paramDecl->getTypeInContext()); + + auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr}); + storedRef = + SubscriptExpr::create(ctx, pointeePropertyRefExpr, argList, subscript); + storedRef->setType(LValueType::get(subscript->getElementInterfaceType())); + } else { + // If the base class var has a clang decl, that means it's an access into a + // stored field. Otherwise, we're looking into another base class, so it's a + // another synthesized accessor. + AccessSemantics accessKind = baseClassVar->getClangDecl() + ? AccessSemantics::DirectToStorage + : AccessSemantics::DirectToImplementation; + + storedRef = new (ctx) + MemberRefExpr(pointeePropertyRefExpr, SourceLoc(), baseClassVar, + DeclNameLoc(), /*Implicit=*/true, accessKind); + storedRef->setType( + LValueType::get(cast(baseClassVar)->getTypeInContext())); + } + + auto newValueParamRefExpr = + new (ctx) DeclRefExpr(setterDecl->getParameters()->get(0), DeclNameLoc(), + /*Implicit=*/true); + newValueParamRefExpr->setType( + setterDecl->getParameters()->get(0)->getTypeInContext()); + + auto assignExpr = + new (ctx) AssignExpr(storedRef, SourceLoc(), newValueParamRefExpr, + /*implicit*/ true); + assignExpr->setType(TupleType::getEmpty(ctx)); + + auto body = BraceStmt::create(ctx, SourceLoc(), {assignExpr}, SourceLoc(), + /*implicit*/ true); + return {body, /*isTypeChecked=*/true}; +} + +static std::pair +synthesizeBaseClassFieldAddressSetterBody(AbstractFunctionDecl *afd, + void *context) { + return synthesizeBaseClassFieldGetterOrAddressGetterBody( + afd, context, AccessorKind::MutableAddress); +} + +static SmallVector +makeBaseClassMemberAccessors(DeclContext *declContext, + AbstractStorageDecl *computedVar, + AbstractStorageDecl *baseClassVar) { + auto &ctx = declContext->getASTContext(); + auto computedType = computedVar->getInterfaceType(); + auto contextTy = declContext->mapTypeIntoEnvironment(computedType); + + // Use 'address' or 'mutableAddress' accessors for non-copyable + // types, unless the base accessor returns it by value. + bool useAddress = contextTy->isNoncopyable() && + (baseClassVar->getReadImpl() == ReadImplKind::Stored || + baseClassVar->getAccessor(AccessorKind::Address)); + + ParameterList *bodyParams = nullptr; + if (auto subscript = dyn_cast(baseClassVar)) { + computedType = computedType->getAs()->getResult(); + + auto idxParam = subscript->getIndices()->get(0); + bodyParams = ParameterList::create(ctx, {idxParam}); + } else { + bodyParams = ParameterList::createEmpty(ctx); + } + + auto getterDecl = AccessorDecl::create( + ctx, + /*FuncLoc=*/SourceLoc(), + /*AccessorKeywordLoc=*/SourceLoc(), + useAddress ? AccessorKind::Address : AccessorKind::Get, computedVar, + /*Async=*/false, /*AsyncLoc=*/SourceLoc(), + /*Throws=*/false, + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), bodyParams, + useAddress ? computedType->wrapInPointer(PTK_UnsafePointer) + : computedType, + declContext); + getterDecl->setIsTransparent(true); + getterDecl->copyFormalAccessFrom(computedVar); + getterDecl->setBodySynthesizer(useAddress + ? synthesizeBaseClassFieldAddressGetterBody + : synthesizeBaseClassFieldGetterBody, + baseClassVar); + if (baseClassVar->getWriteImpl() == WriteImplKind::Immutable) + return {getterDecl}; + + auto newValueParam = + new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), + ctx.getIdentifier("newValue"), declContext); + newValueParam->setSpecifier(ParamSpecifier::Default); + newValueParam->setInterfaceType(computedType); + + SmallVector setterParamDecls; + if (!useAddress) + setterParamDecls.push_back(newValueParam); + if (auto subscript = dyn_cast(baseClassVar)) + setterParamDecls.push_back(subscript->getIndices()->get(0)); + ParameterList *setterBodyParams = + ParameterList::create(ctx, setterParamDecls); + + auto setterDecl = AccessorDecl::create( + ctx, + /*FuncLoc=*/SourceLoc(), + /*AccessorKeywordLoc=*/SourceLoc(), + useAddress ? AccessorKind::MutableAddress : AccessorKind::Set, + computedVar, + /*Async=*/false, /*AsyncLoc=*/SourceLoc(), + /*Throws=*/false, + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), setterBodyParams, + useAddress ? computedType->wrapInPointer(PTK_UnsafeMutablePointer) + : TupleType::getEmpty(ctx), + declContext); + setterDecl->setIsTransparent(true); + setterDecl->copyFormalAccessFrom(computedVar); + setterDecl->setBodySynthesizer(useAddress + ? synthesizeBaseClassFieldAddressSetterBody + : synthesizeBaseClassFieldSetterBody, + baseClassVar); + setterDecl->setSelfAccessKind(SelfAccessKind::Mutating); + + return {getterDecl, setterDecl}; +} + +// Clone attributes that have been imported from Clang. +static void cloneImportedAttributes(ValueDecl *fromDecl, ValueDecl *toDecl) { + ASTContext &context = fromDecl->getASTContext(); + for (auto attr : fromDecl->getAttrs()) { + switch (attr->getKind()) { + case DeclAttrKind::Available: { + toDecl->addAttribute(cast(attr)->clone(context, true)); + break; + } + case DeclAttrKind::Custom: { + CustomAttr *cAttr = cast(attr); + toDecl->addAttribute( + CustomAttr::create(context, SourceLoc(), cAttr->getTypeExpr(), + /*owner*/ toDecl, cAttr->getInitContext(), + cAttr->getArgs(), /*implicit*/ true)); + break; + } + case DeclAttrKind::DiscardableResult: { + toDecl->addAttribute(new (context) DiscardableResultAttr(true)); + break; + } + case DeclAttrKind::Effects: { + toDecl->addAttribute(cast(attr)->clone(context)); + break; + } + case DeclAttrKind::Final: { + toDecl->addAttribute(new (context) FinalAttr(true)); + break; + } + case DeclAttrKind::Transparent: { + toDecl->addAttribute(new (context) TransparentAttr(true)); + break; + } + case DeclAttrKind::WarnUnqualifiedAccess: { + toDecl->addAttribute(new (context) WarnUnqualifiedAccessAttr(true)); + break; + } + default: + break; + } + } +} + +static ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext, + ClangInheritanceInfo inheritance) { + AccessLevel access = inheritance.accessForBaseDecl(decl); + ASTContext &context = decl->getASTContext(); + + if (auto fn = dyn_cast(decl)) { + // TODO: function templates are specialized during type checking so to + // support these we need to tell Swift to type check the synthesized bodies. + // TODO: we also currently don't support static functions. That shouldn't be + // too hard. + if (fn->isStatic() || + isa_and_nonnull(fn->getClangDecl())) + return nullptr; + if (auto cxxMethod = + dyn_cast_or_null(fn->getClangDecl())) { + // FIXME: if this function has rvalue this, we won't be able to synthesize + // the accessor correctly (https://github.com/apple/swift/issues/69745). + if (cxxMethod->getRefQualifier() == clang::RefQualifierKind::RQ_RValue) + return nullptr; + } + + auto out = FuncDecl::createImplicit( + context, fn->getStaticSpelling(), fn->getName(), fn->getNameLoc(), + fn->hasAsync(), fn->hasThrows(), fn->getThrownInterfaceType(), + fn->getGenericParams(), fn->getParameters(), + fn->getResultInterfaceType(), newContext); + cloneImportedAttributes(decl, out); + out->setAccess(access); + inheritance.setUnavailableIfNecessary(decl, out); + out->setBodySynthesizer(synthesizeBaseClassMethodBody, fn); + out->setSelfAccessKind(fn->getSelfAccessKind()); + return out; + } + + if (auto subscript = dyn_cast(decl)) { + auto contextTy = newContext->mapTypeIntoEnvironment( + subscript->getElementInterfaceType()); + // Subscripts that return non-copyable types are not yet supported. + // See: https://github.com/apple/swift/issues/70047. + if (contextTy->isNoncopyable()) + return nullptr; + auto out = SubscriptDecl::create( + subscript->getASTContext(), subscript->getName(), + subscript->getStaticLoc(), subscript->getStaticSpelling(), + subscript->getSubscriptLoc(), subscript->getIndices(), + subscript->getNameLoc(), subscript->getElementInterfaceType(), + newContext, subscript->getGenericParams()); + out->setAccess(access); + inheritance.setUnavailableIfNecessary(decl, out); + out->setAccessors(SourceLoc(), + makeBaseClassMemberAccessors(newContext, out, subscript), + SourceLoc()); + out->setImplInfo(subscript->getImplInfo()); + return out; + } + + if (auto var = dyn_cast(decl)) { + auto oldContext = var->getDeclContext(); + auto oldTypeDecl = oldContext->getSelfNominalTypeDecl(); + // FIXME: this is a workaround for rdar://128013193 + if (oldTypeDecl->getAttrs().hasAttribute() && + context.LangOpts.CxxInteropUseOpaquePointerForMoveOnly) + return nullptr; + + auto rawMemory = allocateMemoryForDecl(var->getASTContext(), + sizeof(VarDecl), false); + auto out = + new (rawMemory) VarDecl(var->isStatic(), var->getIntroducer(), + var->getLoc(), var->getName(), newContext); + out->setInterfaceType(var->getInterfaceType()); + out->setIsObjC(var->isObjC()); + out->setIsDynamic(var->isDynamic()); + out->setAccess(access); + inheritance.setUnavailableIfNecessary(decl, out); + out->getASTContext().evaluator.cacheOutput(HasStorageRequest{out}, false); + auto accessors = makeBaseClassMemberAccessors(newContext, out, var); + out->setAccessors(SourceLoc(), accessors, SourceLoc()); + auto isMutable = var->getWriteImpl() == WriteImplKind::Immutable + ? StorageIsNotMutable + : StorageIsMutable; + out->setImplInfo( + accessors[0]->getAccessorKind() == AccessorKind::Address + ? (accessors.size() > 1 + ? StorageImplInfo(ReadImplKind::Address, + WriteImplKind::MutableAddress, + ReadWriteImplKind::MutableAddress) + : StorageImplInfo(ReadImplKind::Address)) + : StorageImplInfo::getComputed(isMutable)); + out->setIsSetterMutating(true); + return out; + } + + if (auto typeAlias = dyn_cast(decl)) { + auto rawMemory = allocateMemoryForDecl( + typeAlias->getASTContext(), sizeof(TypeAliasDecl), false); + auto out = new (rawMemory) + TypeAliasDecl(typeAlias->getStartLoc(), typeAlias->getEqualLoc(), + typeAlias->getName(), typeAlias->getNameLoc(), + typeAlias->getGenericParams(), newContext); + out->setUnderlyingType(typeAlias->getUnderlyingType()); + out->setAccess(access); + inheritance.setUnavailableIfNecessary(decl, out); + return out; + } + + if (auto typeDecl = dyn_cast(decl)) { + auto rawMemory = allocateMemoryForDecl( + typeDecl->getASTContext(), sizeof(TypeAliasDecl), false); + auto out = new (rawMemory) TypeAliasDecl( + typeDecl->getLoc(), typeDecl->getLoc(), typeDecl->getName(), + typeDecl->getLoc(), nullptr, newContext); + out->setUnderlyingType(typeDecl->getDeclaredInterfaceType()); + out->setAccess(access); + inheritance.setUnavailableIfNecessary(decl, out); + return out; + } + + return nullptr; +} + +ValueDecl *ClangImporter::Implementation::importBaseMemberDecl( + ValueDecl *decl, DeclContext *newContext, + ClangInheritanceInfo inheritance) { + + // 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 key = {decl, newContext}; + auto known = clonedBaseMembers.find(key); + if (known == clonedBaseMembers.end()) { + ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance); + handleAmbiguousSwiftName(cloned); + known = clonedBaseMembers.insert({key, cloned}).first; + clonedMembers.insert(std::make_pair(cloned, decl)); + } + + return known->second; +} + +ValueDecl *ClangImporter::Implementation::getOriginalForClonedMember( + const ValueDecl *decl) { + // If this is a cloned decl, we don't want to reclone it + // Otherwise, we may end up with multiple copies of the same method + if (!decl->hasClangNode()) { + // Skip decls with a clang node as those will never be a clone + auto result = clonedMembers.find(decl); + if (result != clonedMembers.end()) + return result->getSecond(); + } + + return nullptr; +} + +size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity( + const ValueDecl *valueDecl) { + if (auto *func = dyn_cast(valueDecl)) { + if (auto *params = func->getParameters()) { + return params->size(); + } + } + return 0; +} + +TinyPtrVector ClangRecordMemberLookup::evaluate( + Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const { + NominalTypeDecl *recordDecl = desc.recordDecl; + NominalTypeDecl *inheritingDecl = desc.inheritingDecl; + DeclName name = desc.name; + ClangInheritanceInfo inheritance = desc.inheritance; + + auto &ctx = recordDecl->getASTContext(); + + // Whether to skip non-public members. Feature::ImportNonPublicCxxMembers says + // to import all non-public members by default; if that is disabled, we only + // import non-public members annotated with SWIFT_PRIVATE_FILEID (since those + // are the only classes that need non-public members.) + auto *cxxRecordDecl = + dyn_cast(inheritingDecl->getClangDecl()); + auto skipIfNonPublic = + !ctx.LangOpts.hasFeature(Feature::ImportNonPublicCxxMembers) && + cxxRecordDecl && importer::getPrivateFileIDAttrs(cxxRecordDecl).empty(); + + auto directResults = evaluateOrDefault( + ctx.evaluator, + ClangDirectLookupRequest({recordDecl, recordDecl->getClangDecl(), name}), + {}); + + // The set of declarations we found. + TinyPtrVector result; + CollectLookupResults collector(name, result); + + // Find the results that are actually a member of "recordDecl". + ClangModuleLoader *clangModuleLoader = ctx.getClangModuleLoader(); + for (auto foundEntry : directResults) { + auto found = cast(foundEntry); + if (dyn_cast(found->getDeclContext()) != + recordDecl->getClangDecl()) + continue; + + // We should not import 'found' if the following are all true: + // + // - Feature::ImportNonPublicCxxMembers is not enabled + // - 'found' is not a member of a SWIFT_PRIVATE_FILEID-annotated class + // - 'found' is a non-public member. + // - 'found' is not a non-inherited FieldDecl; we must import private + // fields because they may affect implicit conformances that iterate + // through all of a struct's fields, e.g., Sendable (#76892). + // + // Note that we can skip inherited FieldDecls because implicit conformances + // handle those separately. + // + // The first two conditions are captured by skipIfNonPublic. The next two + // are conveyed by the following: + auto nonPublic = found->getAccess() == clang::AS_private || + found->getAccess() == clang::AS_protected; + auto noninheritedField = !inheritance && isa(found); + if (skipIfNonPublic && nonPublic && !noninheritedField) + continue; + + // Don't import constructors on foreign reference types. + if (isa(found) && isa(recordDecl)) + continue; + + auto imported = clangModuleLoader->importDeclDirectly(found); + if (!imported) + continue; + + // If this member is found due to inheritance, clone it from the base class + // by synthesizing getters and setters. + if (inheritance) { + imported = clangModuleLoader->importBaseMemberDecl( + cast(imported), inheritingDecl, inheritance); + if (!imported) + continue; + } + + collector.add(cast(imported)); + } + + if (inheritance) { + // For inherited members, add members that are synthesized eagerly, such as + // subscripts. This is not necessary for non-inherited members because those + // should already be in the lookup table. + for (auto member : + cast(recordDecl)->getCurrentMembersWithoutLoading()) { + auto namedMember = dyn_cast(member); + if (!namedMember || !namedMember->hasName() || + namedMember->getName().getBaseName() != name || + clangModuleLoader->getOriginalForClonedMember(namedMember)) + continue; + + auto *imported = clangModuleLoader->importBaseMemberDecl( + namedMember, inheritingDecl, inheritance); + if (!imported) + continue; + + collector.add(imported); + } + } + + // If this is a C++ record, look through any base classes. + const clang::CXXRecordDecl *cxxRecord; + if ((cxxRecord = + dyn_cast(recordDecl->getClangDecl())) && + cxxRecord->isCompleteDefinition()) { + // Capture the arity of already found members in the + // current record, to avoid adding ambiguous members + // from base classes. + llvm::SmallSet foundMethodNames; + for (const auto *valueDecl : result) + foundMethodNames.insert(valueDecl->getName()); + + for (auto base : cxxRecord->bases()) { + if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public) + continue; + + clang::QualType baseType = base.getType(); + if (auto spectType = + dyn_cast(baseType)) + baseType = spectType->desugar(); + if (!isa(baseType.getCanonicalType())) + continue; + + auto *baseRecord = baseType->getAs()->getDecl(); + + if (importer::isSymbolicCircularBase(cxxRecord, baseRecord)) + // Skip circular bases to avoid unbounded recursion + continue; + + 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(import)->getName() == name) + continue; + + auto baseInheritance = ClangInheritanceInfo(inheritance, base); + + // Add Clang members that are imported lazily. + auto baseResults = evaluateOrDefault( + ctx.evaluator, + ClangRecordMemberLookup({cast(import), name, + inheritingDecl, baseInheritance}), + {}); + + for (auto foundInBase : baseResults) { + // Do not add duplicate entry with the same DeclName, + // as that would cause an ambiguous lookup. + if (foundMethodNames.count(foundInBase->getName())) + continue; + + collector.add(foundInBase); + } + } + } + } + + return result; +} From 503675a0f4e3e2b062ea95042f25cb64125401bd Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 18 Dec 2025 23:15:34 -0800 Subject: [PATCH 6/6] [cxx-interop] [gardening] Clean up #includes in ClangImporter.cpp --- lib/ClangImporter/ClangImporter.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 030e21528a08d..c46fb828c8a99 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -13,7 +13,6 @@ // This file implements support for loading Clang modules into Swift. // //===----------------------------------------------------------------------===// -#include "swift/ClangImporter/ClangImporter.h" #include "CFTypeInfo.h" #include "ClangDerivedConformances.h" #include "ClangDiagnosticConsumer.h" @@ -29,7 +28,6 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/Evaluator.h" -#include "swift/AST/IRGenOptions.h" #include "swift/AST/ImportCache.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" @@ -45,13 +43,11 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/Platform.h" -#include "swift/Basic/Range.h" #include "swift/Basic/SourceLoc.h" -#include "swift/Basic/StringExtras.h" #include "swift/Basic/Version.h" +#include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterRequests.h" #include "swift/ClangImporter/ClangModule.h" -#include "swift/Frontend/CompileJobCacheKey.h" #include "swift/Parse/ParseVersion.h" #include "swift/Strings.h" #include "swift/Subsystems.h"