From fb3b479cf7c0bcd04e5543d038c0dc8b4a661d02 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Mon, 30 Sep 2024 07:28:50 +0100 Subject: [PATCH] [cxx-interop] Support importing swift::String Whenever the String Swift type is exported to C++, we create swift::String on the C++ side. However, we did not support importing swift::String back into Swift. This PR addresses this problem. Unfortunately, we cannot just set swift::String as the ClangNode of Swift's String type. Types with ClangNodes are using the foreign representation, but in this case we want to use the native type whenever possible (e.g., for codegen). To work this around, this PR updates ClangTypeConverter to make sure we have the correct Clang types when we emit function calls to C++ APIs that use swift::String in their interface. --- include/swift/AST/ASTContext.h | 2 + lib/AST/ASTContext.cpp | 8 ++++ lib/AST/ClangTypeConverter.cpp | 15 +++++++ lib/AST/ClangTypeConverter.h | 1 + lib/ClangImporter/ImportDecl.cpp | 23 ++++++---- ...stdlib-types-back-to-swift-execution.swift | 42 +++++++++++++++++++ 6 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 test/Interop/SwiftToCxxToSwift/import-swift-stdlib-types-back-to-swift-execution.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 16c59acfbeb18..921241b09ed10 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -843,6 +843,8 @@ class ASTContext final { /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); + void registerExportedClangDecl(Decl *decl, const clang::Decl *clangDecl); + /// General conversion method from Swift types -> Clang types. /// /// HACK: This method is only intended to be called from a specific place in diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d9bd1d54aafe7..4da1f42057dfc 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -6239,6 +6239,14 @@ ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { return impl.Converter->getSwiftDeclForExportedClangDecl(decl); } +void ASTContext::registerExportedClangDecl(Decl *decl, + const clang::Decl *clangDecl) { + // Make sure we have a converter. + getClangTypeConverter(); + auto &impl = getImpl(); + impl.Converter->registerExportedClangDecl(decl, clangDecl); +} + const clang::Type * ASTContext::getClangTypeForIRGen(Type ty) { return getClangTypeConverter().convert(ty).getTypePtrOrNull(); diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 5b56c90f40b28..d519e0cbcb935 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -36,6 +36,8 @@ #include "swift/Basic/LLVM.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Sema.h" @@ -850,6 +852,18 @@ clang::QualType ClangTypeConverter::convert(Type type) { 1); return ctx.getObjCObjectPointerType(clangType); } + } else { + auto it = ReversedExportMapBackwards.find(decl); + // This can happen, when the String type is exported to C++ (as + // swift::String) which is later imported back to Swift as String. + if (it != ReversedExportMapBackwards.end()) { + const auto *clangDecl = it->getSecond(); + const auto *dc = clangDecl->getDeclContext(); + if (dc->isNamespace() && + cast(dc)->getName() == "swift") + return clang::QualType( + cast(clangDecl)->getTypeForDecl(), 0); + } } } @@ -865,6 +879,7 @@ void ClangTypeConverter::registerExportedClangDecl(Decl *swiftDecl, "generated Clang declaration for Swift declaration should not " "have multiple declarations"); ReversedExportMap.insert({clangDecl, swiftDecl}); + ReversedExportMapBackwards.insert({swiftDecl, clangDecl}); } Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index 0396233a1938b..4c46bf595e178 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -34,6 +34,7 @@ class ClangTypeConverter : llvm::DenseMap Cache; llvm::DenseMap ReversedExportMap; + llvm::DenseMap ReversedExportMapBackwards; bool StdlibTypesAreCached = false; diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 24ca939f8dc0b..441331618cdb5 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2746,15 +2746,25 @@ namespace { return nullptr; } + auto dc = decl->getDeclContext(); // Bail if this is `std::chrono::tzdb`. This type causes issues in copy // constructor instantiation. // FIXME: https://github.com/apple/swift/issues/73037 - if (decl->getDeclContext()->isNamespace() && - decl->getDeclContext()->getParent()->isStdNamespace() && + if (dc->isNamespace() && dc->getParent()->isStdNamespace() && decl->getIdentifier() && (decl->getName() == "tzdb" || decl->getName() == "time_zone_link")) return nullptr; + // Import the C++ swift::String as the swift String type. + if (dc->isNamespace() && + cast(dc)->getName() == "swift" && + decl->getIdentifier() && decl->getName() == "String") { + auto swiftDecl = Impl.SwiftContext.getStringDecl(); + Impl.SwiftContext.registerExportedClangDecl(swiftDecl, + decl->getCanonicalDecl()); + return swiftDecl; + } + auto &clangSema = Impl.getClangSema(); // Make Clang define any implicit constructors it may need (copy, // default). Make sure we only do this if the class has been fully defined @@ -3021,15 +3031,14 @@ namespace { Decl *VisitClassTemplateSpecializationDecl( const clang::ClassTemplateSpecializationDecl *decl) { + auto *classTemplate = decl->getSpecializedTemplate(); // Treat a specific specialization like the unspecialized class template // when importing it in symbolic mode. if (Impl.importSymbolicCXXDecls) - return Impl.importDecl(decl->getSpecializedTemplate(), - Impl.CurrentVersion); - - bool isPair = decl->getSpecializedTemplate()->isInStdNamespace() && - decl->getSpecializedTemplate()->getName() == "pair"; + return Impl.importDecl(classTemplate,Impl.CurrentVersion); + bool isPair = classTemplate->isInStdNamespace() && + classTemplate->getName() == "pair"; // Before we go any further, check if we've already got tens of thousands // of specializations. If so, it means we're likely instantiating a very // deep/complex template, or we've run into an infinite loop. In either diff --git a/test/Interop/SwiftToCxxToSwift/import-swift-stdlib-types-back-to-swift-execution.swift b/test/Interop/SwiftToCxxToSwift/import-swift-stdlib-types-back-to-swift-execution.swift new file mode 100644 index 0000000000000..6aabf469f067a --- /dev/null +++ b/test/Interop/SwiftToCxxToSwift/import-swift-stdlib-types-back-to-swift-execution.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod.h -I %t -enable-experimental-cxx-interop -Xcc -DFIRSTPASS + +// RUN: %target-interop-build-swift %t/swiftMod.swift -o %t/swift-execution -module-name SwiftMod -I %t -g -DSECOND_PASS -Xcc -DSWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR + +// RUN: %target-codesign %t/swift-execution +// RUN: %target-run %t/swift-execution | %FileCheck %s + +// REQUIRES: executable_test + +//--- header.h +#ifndef FIRSTPASS + +#include "swiftMod.h" + +inline swift::String createString() { + return swift::String("Foobar"); +} + +#endif + +//--- module.modulemap +module SwiftToCxxTest { + header "header.h" + requires cplusplus +} + +//--- swiftMod.swift +import SwiftToCxxTest + +public func f() -> String? { "" } + +#if SECOND_PASS + +let str = createString() +print(str) + +#endif + +// CHECK: Foobar