From a45d03a6694c29995471bdd258f241201efc2c6b Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Wed, 25 Oct 2023 19:12:43 +0100 Subject: [PATCH] [cxx-interop] Use unique mangling for distinct C++ class template specializations This makes sure we are printing more than one level of C++ template specializations when emitting a Swift struct name. For instance, `std::__wrap_iter` and `std::__wrap_iter` are currently imported with the same name in Swift. This means the mangled string will be the same for these specializations, despite them being distinct types. This causes mangling errors. rdar://117485399 --- lib/ClangImporter/CMakeLists.txt | 1 + .../ClangClassTemplateNamePrinter.cpp | 147 ++++++++++++++++++ .../ClangClassTemplateNamePrinter.h | 40 +++++ lib/ClangImporter/ImportName.cpp | 109 +------------ .../class-template-with-primitive-argument.h | 3 + ...-primitive-argument-module-interface.swift | 10 ++ test/SILGen/opaque_values_cxx.swift | 8 +- 7 files changed, 209 insertions(+), 109 deletions(-) create mode 100644 lib/ClangImporter/ClangClassTemplateNamePrinter.cpp create mode 100644 lib/ClangImporter/ClangClassTemplateNamePrinter.h create mode 100644 test/Interop/Cxx/templates/class-template-with-primitive-argument-module-interface.swift diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index ad4fce4172d9a..3c88e43bac1ca 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -8,6 +8,7 @@ add_gyb_target(generated_sorted_cf_database add_swift_host_library(swiftClangImporter STATIC CFTypeInfo.cpp ClangAdapter.cpp + ClangClassTemplateNamePrinter.cpp ClangDerivedConformances.cpp ClangDiagnosticConsumer.cpp ClangImporter.cpp diff --git a/lib/ClangImporter/ClangClassTemplateNamePrinter.cpp b/lib/ClangImporter/ClangClassTemplateNamePrinter.cpp new file mode 100644 index 0000000000000..a005aeb77c48c --- /dev/null +++ b/lib/ClangImporter/ClangClassTemplateNamePrinter.cpp @@ -0,0 +1,147 @@ +//===--- ClangClassTemplateNamePrinter.cpp --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 +// +//===----------------------------------------------------------------------===// + +#include "ClangClassTemplateNamePrinter.h" +#include "ImporterImpl.h" +#include "clang/AST/TypeVisitor.h" + +using namespace swift; +using namespace swift::importer; + +struct TemplateInstantiationNamePrinter + : clang::TypeVisitor { + ASTContext &swiftCtx; + NameImporter *nameImporter; + ImportNameVersion version; + + TemplateInstantiationNamePrinter(ASTContext &swiftCtx, + NameImporter *nameImporter, + ImportNameVersion version) + : swiftCtx(swiftCtx), nameImporter(nameImporter), version(version) {} + + std::string VisitBuiltinType(const clang::BuiltinType *type) { + Type swiftType = nullptr; + switch (type->getKind()) { + case clang::BuiltinType::Void: + swiftType = + swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), "Void"); + break; +#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ + case clang::BuiltinType::CLANG_BUILTIN_KIND: \ + swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \ + #SWIFT_TYPE_NAME); \ + break; +#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ + case clang::BuiltinType::CLANG_BUILTIN_KIND: \ + swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \ + #SWIFT_TYPE_NAME); \ + break; +#include "swift/ClangImporter/BuiltinMappedTypes.def" + default: + break; + } + + if (swiftType) { + if (swiftType->is()) { + return swiftType->getStringAsComponent(); + } + } + return "_"; + } + + std::string VisitRecordType(const clang::RecordType *type) { + auto tagDecl = type->getAsTagDecl(); + if (auto namedArg = dyn_cast_or_null(tagDecl)) { + llvm::SmallString<128> storage; + llvm::raw_svector_ostream buffer(storage); + nameImporter->importName(namedArg, version, clang::DeclarationName()) + .getDeclName() + .print(buffer); + return buffer.str().str(); + } + return "_"; + } + + std::string VisitPointerType(const clang::PointerType *type) { + std::string pointeeResult = Visit(type->getPointeeType().getTypePtr()); + + enum class TagTypeDecorator { None, UnsafePointer, UnsafeMutablePointer }; + + // If this is a pointer to foreign reference type, we should not wrap + // it in Unsafe(Mutable)?Pointer, since it will be imported as a class + // in Swift. + bool isReferenceType = false; + if (auto tagDecl = type->getPointeeType()->getAsTagDecl()) { + if (auto *rd = dyn_cast(tagDecl)) + isReferenceType = + ClangImporter::Implementation::recordHasReferenceSemantics( + rd, swiftCtx); + } + + TagTypeDecorator decorator; + if (!isReferenceType) + decorator = type->getPointeeType().isConstQualified() + ? TagTypeDecorator::UnsafePointer + : TagTypeDecorator::UnsafeMutablePointer; + else + decorator = TagTypeDecorator::None; + + llvm::SmallString<128> storage; + llvm::raw_svector_ostream buffer(storage); + if (decorator != TagTypeDecorator::None) + buffer << (decorator == TagTypeDecorator::UnsafePointer + ? "UnsafePointer" + : "UnsafeMutablePointer") + << '<'; + buffer << pointeeResult; + if (decorator != TagTypeDecorator::None) + buffer << '>'; + + return buffer.str().str(); + } +}; + +std::string swift::importer::printClassTemplateSpecializationName( + const clang::ClassTemplateSpecializationDecl *decl, ASTContext &swiftCtx, + NameImporter *nameImporter, ImportNameVersion version) { + TemplateInstantiationNamePrinter templateNamePrinter(swiftCtx, nameImporter, + version); + + // TODO: the following logic should probably be a ConstTemplateArgumentVisitor + llvm::SmallString<128> storage; + llvm::raw_svector_ostream buffer(storage); + decl->printName(buffer); + buffer << "<"; + llvm::interleaveComma( + decl->getTemplateArgs().asArray(), buffer, + [&buffer, &templateNamePrinter](const clang::TemplateArgument &arg) { + // Use import name here so builtin types such as "int" map to their + // Swift equivalent ("CInt"). + if (arg.getKind() == clang::TemplateArgument::Type) { + auto ty = arg.getAsType().getTypePtr(); + buffer << templateNamePrinter.Visit(ty); + return; + } else if (arg.getKind() == clang::TemplateArgument::Integral) { + buffer << "_"; + if (arg.getIntegralType()->isBuiltinType()) { + buffer << templateNamePrinter.Visit( + arg.getIntegralType().getTypePtr()) + << "_"; + } + arg.getAsIntegral().print(buffer, true); + return; + } + buffer << "_"; + }); + buffer << ">"; + return buffer.str().str(); +} diff --git a/lib/ClangImporter/ClangClassTemplateNamePrinter.h b/lib/ClangImporter/ClangClassTemplateNamePrinter.h new file mode 100644 index 0000000000000..f93975bb1f3c7 --- /dev/null +++ b/lib/ClangImporter/ClangClassTemplateNamePrinter.h @@ -0,0 +1,40 @@ +//===--- ClangClassTemplateNamePrinter.h ------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H +#define SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H + +#include "ImportName.h" +#include "swift/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" + +namespace swift { +namespace importer { + +/// Returns a Swift representation of a C++ class template specialization name, +/// e.g. "vector>". +/// +/// This expands the entire tree of template instantiation names recursively. +/// While printing deep instantiation levels might not increase readability, it +/// is important to do because the C++ templated class names get mangled, +/// therefore they must be unique for different instantiations. +/// +/// This function does not instantiate any templates and does not modify the AST +/// in any way. +std::string printClassTemplateSpecializationName( + const clang::ClassTemplateSpecializationDecl *decl, ASTContext &swiftCtx, + NameImporter *nameImporter, ImportNameVersion version); + +} // namespace importer +} // namespace swift + +#endif // SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 7dd5689a98bd9..fc89ac152553d 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "CFTypeInfo.h" +#include "ClangClassTemplateNamePrinter.h" #include "ClangDiagnosticConsumer.h" #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" @@ -2214,111 +2215,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, return importNameImpl(classTemplateSpecDecl->getSpecializedTemplate(), version, givenName); if (!isa(D)) { - auto getSwiftBuiltinTypeName = - [&](const clang::BuiltinType *builtin) -> std::optional { - Type swiftType = nullptr; - switch (builtin->getKind()) { - case clang::BuiltinType::Void: - swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), - "Void"); - break; -#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ - case clang::BuiltinType::CLANG_BUILTIN_KIND: \ - swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \ - #SWIFT_TYPE_NAME); \ - break; -#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ - case clang::BuiltinType::CLANG_BUILTIN_KIND: \ - swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \ - #SWIFT_TYPE_NAME); \ - break; -#include "swift/ClangImporter/BuiltinMappedTypes.def" - default: - break; - } - - if (swiftType) { - if (swiftType->is()) { - return swiftType->getStringAsComponent(); - } - } - return std::nullopt; - }; - - // When constructing the name of a C++ template, don't expand all the - // template, only expand one layer. Here we want to prioritize - // readability over total completeness. - llvm::SmallString<128> storage; - llvm::raw_svector_ostream buffer(storage); - D->printName(buffer); - buffer << "<"; - llvm::interleaveComma(classTemplateSpecDecl->getTemplateArgs().asArray(), - buffer, - [&buffer, this, version, &getSwiftBuiltinTypeName](const clang::TemplateArgument& arg) { - // Use import name here so builtin types such as "int" map to their - // Swift equivalent ("Int32"). - if (arg.getKind() == clang::TemplateArgument::Type) { - auto ty = arg.getAsType().getTypePtr(); - if (auto builtin = dyn_cast(ty)) { - if (auto swiftTypeName = getSwiftBuiltinTypeName(builtin)) { - buffer << *swiftTypeName; - return; - } - } else { - // FIXME: Generalize this to cover pointer to - // builtin type too. - // Check if this a struct/class - // or a pointer/reference to a struct/class. - auto *tagDecl = ty->getAsTagDecl(); - enum class TagTypeDecorator { - None, - UnsafePointer, - UnsafeMutablePointer - }; - TagTypeDecorator decorator = TagTypeDecorator::None; - if (!tagDecl && ty->isPointerType()) { - tagDecl = ty->getPointeeType()->getAsTagDecl(); - if (tagDecl) { - bool isReferenceType = false; - if (auto *rd = dyn_cast(tagDecl)) - isReferenceType = ClangImporter::Implementation:: - recordHasReferenceSemantics(rd, swiftCtx); - if (!isReferenceType) - decorator = ty->getPointeeType().isConstQualified() - ? TagTypeDecorator::UnsafePointer - : TagTypeDecorator::UnsafeMutablePointer; - } - } - if (auto namedArg = dyn_cast_or_null(tagDecl)) { - if (decorator != TagTypeDecorator::None) - buffer << (decorator == TagTypeDecorator::UnsafePointer - ? "UnsafePointer" - : "UnsafeMutablePointer") - << '<'; - importNameImpl(namedArg, version, clang::DeclarationName()) - .getDeclName() - .print(buffer); - if (decorator != TagTypeDecorator::None) - buffer << '>'; - return; - } - } - } else if (arg.getKind() == clang::TemplateArgument::Integral) { - buffer << "_"; - if (arg.getIntegralType()->isBuiltinType()) { - if (auto swiftTypeName = getSwiftBuiltinTypeName( - arg.getIntegralType()->getAs())) { - buffer << *swiftTypeName << "_"; - } - } - arg.getAsIntegral().print(buffer, true); - return; - } - buffer << "_"; - }); - buffer << ">"; - - baseName = swiftCtx.getIdentifier(buffer.str()).get(); + auto name = printClassTemplateSpecializationName(classTemplateSpecDecl, + swiftCtx, this, version); + baseName = swiftCtx.getIdentifier(name).get(); } } diff --git a/test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h b/test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h index d791b430bfa41..fde51925cb550 100644 --- a/test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h +++ b/test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h @@ -8,5 +8,8 @@ struct MagicWrapper { }; typedef MagicWrapper WrappedMagicInt; +typedef MagicWrapper WrappedMagicIntPtr; +typedef MagicWrapper WrappedMagicIntConstPtr; +typedef MagicWrapper WrappedMagicIntPtrPtr; #endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_WITH_PRIMITIVE_ARGUMENT_H diff --git a/test/Interop/Cxx/templates/class-template-with-primitive-argument-module-interface.swift b/test/Interop/Cxx/templates/class-template-with-primitive-argument-module-interface.swift new file mode 100644 index 0000000000000..a7756984daf31 --- /dev/null +++ b/test/Interop/Cxx/templates/class-template-with-primitive-argument-module-interface.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateWithPrimitiveArgument -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s + +// CHECK: @available(*, unavailable +// CHECK: struct MagicWrapper { +// CHECK: } + +// CHECK: typealias WrappedMagicInt = MagicWrapper +// CHECK: typealias WrappedMagicIntPtr = MagicWrapper> +// CHECK: typealias WrappedMagicIntConstPtr = MagicWrapper> +// CHECK: typealias WrappedMagicIntPtrPtr = MagicWrapper>> diff --git a/test/SILGen/opaque_values_cxx.swift b/test/SILGen/opaque_values_cxx.swift index 545375056e761..76352bc28abfb 100644 --- a/test/SILGen/opaque_values_cxx.swift +++ b/test/SILGen/opaque_values_cxx.swift @@ -15,12 +15,12 @@ import Cxx // CHECK: end_borrow [[VECTOR]] // CHECK: return [[BEGIN]] // CHECK-LABEL: } // end sil function '$sSo3stdO3__1O0055vectorCUnsignedIntallocatorCUnsignedInt_iqGBpboaivxaEhaV3Cxx0B8SequenceSCAgHP13__beginUnsafe11RawIteratorQzyFTW' -// CHECK-LABEL: sil {{.*}}[ossa] @$sSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaVSQSCSQ2eeoiySbx_xtFZTW : {{.*}} { -// CHECK: bb0([[LHS:%[^,]+]] : $std.__1.__wrap_iter<_>, [[RHS:%[^,]+]] : -// CHECK: [[CALLEE:%[^,]+]] = function_ref @$sSo2eeoiySbSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaV_AGtFTO +// CHECK-LABEL: sil {{.*}}[ossa] @$sSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaVSQSCSQ2eeoiySbx_xtFZTW : {{.*}} { +// CHECK: bb0([[LHS:%[^,]+]] : $std.__1.__wrap_iter>, [[RHS:%[^,]+]] : +// CHECK: [[CALLEE:%[^,]+]] = function_ref @$sSo2eeoiySbSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaV_AGtFTO // CHECK: [[EQUAL:%[^,]+]] = apply [[CALLEE]]([[LHS]], [[RHS]]) // CHECK: return [[EQUAL]] -// CHECK-LABEL: } // end sil function '$sSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaVSQSCSQ2eeoiySbx_xtFZTW' +// CHECK-LABEL: } // end sil function '$sSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaVSQSCSQ2eeoiySbx_xtFZTW' func test_cxx_vector_uint32t_iterate(_ n: Int, _ vectorOfU32: VectorOfU32) { for x in vectorOfU32 {} }