Skip to content

Commit cb537b4

Browse files
hlopkoscentinigribozavrCodaFi
authored
[cxx-interop] Import typedef-ed template instantiations #32950 (#33451)
This is a roll-forward of #32950, with explicit c++17 version removed from tests. This is not needed since C++17 is the default anyway. -- In this PR we teach `ClangImporter` to import typedef statements with template instantiation as its underlying type. ```c++ template<class T> struct MagicWrapper { T t; }; struct MagicNumber {}; typedef MagicWrapper<MagicNumber> WrappedMagicNumber; ``` will be made available in Swift as if `WrappedMagicNumber` is a regular struct. In C++, multiple distinct typedeffed instantiations resolve to the same canonical type. We implement this by creating a hidden intermediate struct that typedef aliasses. The struct is named as `__CxxTemplateInst` plus Itanium mangled type of the instantiation. For the example above the name of the hidden struct is `__CxxTemplateInst12MagicWrapperI11MagicNumberE`. Double underscore (denoting a reserved C++ identifier) is used to discourage direct usage. We chose Itanium mangling scheme because it produces valid Swift identifiers and covers all C++ edge cases. Imported module interface of the example above: ```swift struct __CxxTemplateInst12MagicWrapperI11MagicNumberE { var t: MagicNumber } struct MagicNumber {} typealias WrappedMagicNumber = __CxxTemplateInst12MagicWrapperI11MagicNumberE ``` We modified the `SwiftLookupTable` logic to show hidden structs in `swift_ide_test` for convenience. Co-authored-by: Rosica Dejanovska <rosica@google.com> Co-authored-by: Dmitri Gribenko <gribozavr@gmail.com> Co-authored-by: Robert Widmann <devteam.codafi@gmail.com>
1 parent dc8bc70 commit cb537b4

36 files changed

+724
-9
lines changed

docs/ABI/Mangling.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,3 +1107,40 @@ nominal type descriptor symbol for ``CxxStruct`` while compiling the ``main`` mo
11071107
.. code::
11081108
11091109
sSo9CxxStructVMn // -> nominal type descriptor for __C.CxxStruct
1110+
1111+
Importing C++ class template instantiations
1112+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1113+
1114+
A class template instantiation is imported as a struct named
1115+
``__CxxTemplateInst`` plus Itanium mangled type of the instantiation (see the
1116+
``type`` production in the Itanium specification). Note that Itanium mangling is
1117+
used on all platforms, regardless of the ABI of the C++ toolchain, to ensure
1118+
that the mangled name is a valid Swift type name (this is not the case for MSVC
1119+
mangled names). A prefix with a double underscore (to ensure we have a reserved
1120+
C++ identifier) is added to limit the possibility for conflicts with names of
1121+
user-defined structs. The struct is notionally defined in the ``__C`` module,
1122+
similarly to regular C and C++ structs and classes. Consider the following C++
1123+
module:
1124+
1125+
.. code-block:: c++
1126+
1127+
template<class T>
1128+
struct MagicWrapper {
1129+
T t;
1130+
};
1131+
1132+
struct MagicNumber {};
1133+
1134+
typedef MagicWrapper<MagicNumber> WrappedMagicNumber;
1135+
1136+
``WrappedMagicNumber`` is imported as a typealias for struct
1137+
``__CxxTemplateInst12MagicWrapperI11MagicNumberE``. Interface of the imported
1138+
module looks as follows:
1139+
1140+
.. code-block:: swift
1141+
1142+
struct __CxxTemplateInst12MagicWrapperI11MagicNumberE {
1143+
var t: MagicNumber
1144+
}
1145+
struct MagicNumber {}
1146+
typealias WrappedMagicNumber = __CxxTemplateInst12MagicWrapperI11MagicNumberE

docs/CppInteroperabilityManifesto.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Assumptions:
6767
+ [Function templates: calls with generic type parameters](#function-templates-calls-with-generic-type-parameters)
6868
+ [Function templates: importing as real generic functions](#function-templates-importing-as-real-generic-functions)
6969
+ [Class templates](#class-templates)
70+
+ [Class templates: importing instantiation behind typedef](#class-templates-importing-instantiation-behind-typedef)
7071
+ [Class templates: importing specific specilalizations](#class-templates-importing-specific-specilalizations)
7172
+ [Class templates: using with generic type parameters](#class-templates-using-with-generic-type-parameters)
7273
+ [Class templates: using in generic code through a synthesized protocol](#class-templates-using-in-generic-code-through-a-synthesized-protocol)
@@ -2575,6 +2576,45 @@ We could ignore explicit specializations of function templates, because they
25752576
don't affect the API. Explicit specializations of class templates can
25762577
dramatically change the API of the type.
25772578

2579+
### Class templates: Importing full class template instantiations
2580+
2581+
A class template instantiation could be imported as a struct named
2582+
`__CxxTemplateInst` plus Itanium mangled type of the instantiation (see the
2583+
`type` production in the Itanium specification). Note that Itanium mangling is
2584+
used on all platforms, regardless of the ABI of the C++ toolchain, to ensure
2585+
that the mangled name is a valid Swift type name (this is not the case for MSVC
2586+
mangled names). A prefix with a double underscore (to ensure we have a reserved
2587+
C++ identifier) is added to limit the possibility for conflicts with names of
2588+
user-defined structs. The struct is notionally defined in the `__C` module,
2589+
similarly to regular C and C++ structs and classes. Consider the following C++
2590+
module:
2591+
2592+
```c++
2593+
// C++ header.
2594+
2595+
template<class T>
2596+
struct MagicWrapper {
2597+
T t;
2598+
};
2599+
struct MagicNumber {};
2600+
2601+
typedef MagicWrapper<MagicNumber> WrappedMagicNumber;
2602+
```
2603+
2604+
`WrappedMagicNumber` will be imported as a typealias for a struct
2605+
`__CxxTemplateInst12MagicWrapperI11MagicNumberE`. Interface of the imported
2606+
module will look as follows:
2607+
2608+
```swift
2609+
// C++ header imported to Swift.
2610+
2611+
struct __CxxTemplateInst12MagicWrapperI11MagicNumberE {
2612+
var t: MagicNumber
2613+
}
2614+
struct MagicNumber {}
2615+
typealias WrappedMagicNumber = __CxxTemplateInst12MagicWrapperI11MagicNumberE
2616+
```
2617+
25782618
### Class templates: importing specific specilalizations
25792619

25802620
Just like with calls to C++ function templates, it is easy to compile a use of a
@@ -2752,7 +2792,7 @@ func useConcrete() {
27522792

27532793
### Class templates: importing as real generic structs
27542794

2755-
If we know the complete set of allowed type arguments to a C++ function
2795+
If we know the complete set of allowed type arguments to a C++ struct
27562796
template, we could import it as an actual Swift generic struct. Every method of
27572797
that struct will perform dynamic dispatch based on type parameters. See
27582798
the section about function templates for more details.

include/swift/Strings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ constexpr static const StringLiteral MANGLING_MODULE_OBJC = "__C";
3838
constexpr static const StringLiteral MANGLING_MODULE_CLANG_IMPORTER =
3939
"__C_Synthesized";
4040

41+
/// The name prefix for C++ template instantiation imported as a Swift struct.
42+
constexpr static const StringLiteral CXX_TEMPLATE_INST_PREFIX =
43+
"__CxxTemplateInst";
44+
4145
constexpr static const StringLiteral SEMANTICS_PROGRAMTERMINATION_POINT =
4246
"programtermination_point";
4347

lib/AST/ASTMangler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "clang/AST/Attr.h"
4040
#include "clang/AST/Decl.h"
4141
#include "clang/AST/DeclObjC.h"
42+
#include "clang/AST/DeclTemplate.h"
4243
#include "clang/AST/Mangle.h"
4344
#include "llvm/ADT/DenseMap.h"
4445
#include "llvm/ADT/SmallString.h"
@@ -2114,6 +2115,7 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
21142115
// Always use Clang names for imported Clang declarations, unless they don't
21152116
// have one.
21162117
auto tryAppendClangName = [this, decl]() -> bool {
2118+
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
21172119
auto namedDecl = getClangDeclForMangling(decl);
21182120
if (!namedDecl)
21192121
return false;
@@ -2126,6 +2128,13 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
21262128
appendIdentifier(interface->getObjCRuntimeNameAsString());
21272129
} else if (UseObjCRuntimeNames && protocol) {
21282130
appendIdentifier(protocol->getObjCRuntimeNameAsString());
2131+
} else if (auto ctsd = dyn_cast<clang::ClassTemplateSpecializationDecl>(namedDecl)) {
2132+
// If this is a `ClassTemplateSpecializationDecl`, it was
2133+
// imported as a Swift decl with `__CxxTemplateInst...` name.
2134+
// `ClassTemplateSpecializationDecl`'s name does not include information about
2135+
// template arguments, and in order to prevent name clashes we use the
2136+
// name of the Swift decl which does include template arguments.
2137+
appendIdentifier(nominal->getName().str());
21292138
} else {
21302139
appendIdentifier(namedDecl->getName());
21312140
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,14 +3477,33 @@ namespace {
34773477

34783478
Decl *VisitClassTemplateSpecializationDecl(
34793479
const clang::ClassTemplateSpecializationDecl *decl) {
3480-
// FIXME: We could import specializations, but perhaps only as unnamed
3481-
// structural types.
3482-
return nullptr;
3480+
// `Sema::isCompleteType` will try to instantiate the class template as a
3481+
// side-effect and we rely on this here. `decl->getDefinition()` can
3482+
// return nullptr before the call to sema and return its definition
3483+
// afterwards.
3484+
if (!Impl.getClangSema().isCompleteType(
3485+
decl->getLocation(),
3486+
Impl.getClangASTContext().getRecordType(decl))) {
3487+
// If we got nullptr definition now it means the type is not complete.
3488+
// We don't import incomplete types.
3489+
return nullptr;
3490+
}
3491+
auto def = dyn_cast<clang::ClassTemplateSpecializationDecl>(
3492+
decl->getDefinition());
3493+
assert(def && "Class template instantiation didn't have definition");
3494+
// FIXME: This will instantiate all members of the specialization (and detect
3495+
// instantiation failures in them), which can be more than is necessary
3496+
// and is more than what Clang does. As a result we reject some C++
3497+
// programs that Clang accepts.
3498+
Impl.getClangSema().InstantiateClassTemplateSpecializationMembers(
3499+
def->getLocation(), def, clang::TSK_ExplicitInstantiationDefinition);
3500+
3501+
return VisitRecordDecl(def);
34833502
}
34843503

34853504
Decl *VisitClassTemplatePartialSpecializationDecl(
3486-
const clang::ClassTemplatePartialSpecializationDecl *decl) {
3487-
// Note: templates are not imported.
3505+
const clang::ClassTemplatePartialSpecializationDecl *decl) {
3506+
// Note: partial template specializations are not imported.
34883507
return nullptr;
34893508
}
34903509

lib/ClangImporter/ImportName.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
#include "swift/Basic/StringExtras.h"
3232
#include "swift/ClangImporter/ClangImporterOptions.h"
3333
#include "swift/Parse/Parser.h"
34+
#include "swift/Strings.h"
3435
#include "clang/AST/ASTContext.h"
3536
#include "clang/AST/DeclCXX.h"
37+
#include "clang/AST/Mangle.h"
3638
#include "clang/Basic/IdentifierTable.h"
3739
#include "clang/Basic/Module.h"
3840
#include "clang/Basic/OperatorKinds.h"
@@ -1698,6 +1700,34 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
16981700
}
16991701
}
17001702

1703+
if (auto classTemplateSpecDecl =
1704+
dyn_cast<clang::ClassTemplateSpecializationDecl>(D)) {
1705+
if (!isa<clang::ClassTemplatePartialSpecializationDecl>(D)) {
1706+
1707+
auto &astContext = classTemplateSpecDecl->getASTContext();
1708+
// Itanium mangler produces valid Swift identifiers, use it to generate a name for
1709+
// this instantiation.
1710+
clang::MangleContext *mangler = clang::ItaniumMangleContext::create(
1711+
astContext, astContext.getDiagnostics());
1712+
llvm::SmallString<128> storage;
1713+
llvm::raw_svector_ostream buffer(storage);
1714+
mangler->mangleTypeName(astContext.getRecordType(classTemplateSpecDecl),
1715+
buffer);
1716+
1717+
// The Itanium mangler does not provide a way to get the mangled
1718+
// representation of a type. Instead, we call mangleTypeName() that
1719+
// returns the name of the RTTI typeinfo symbol, and remove the _ZTS
1720+
// prefix. Then we prepend __CxxTemplateInst to reduce chances of conflict
1721+
// with regular C and C++ structs.
1722+
llvm::SmallString<128> mangledNameStorage;
1723+
llvm::raw_svector_ostream mangledName(mangledNameStorage);
1724+
assert(buffer.str().take_front(4) == "_ZTS");
1725+
mangledName << CXX_TEMPLATE_INST_PREFIX << buffer.str().drop_front(4);
1726+
1727+
baseName = swiftCtx.getIdentifier(mangledName.str()).get();
1728+
}
1729+
}
1730+
17011731
// swift_newtype-ed declarations may have common words with the type name
17021732
// stripped.
17031733
if (auto newtypeDecl = findSwiftNewtype(D, clangSema, version)) {

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,10 +1881,16 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
18811881
// struct names when relevant, not just pointer names. That way we can check
18821882
// both CFDatabase.def and the objc_bridge attribute and cover all our bases.
18831883
if (auto *tagDecl = dyn_cast<clang::TagDecl>(named)) {
1884-
if (!tagDecl->getDefinition())
1884+
// We add entries for ClassTemplateSpecializations that don't have
1885+
// definition. It's possible that the decl will be instantiated by
1886+
// SwiftDeclConverter later on. We cannot force instantiating
1887+
// ClassTemplateSPecializations here because we're currently writing the
1888+
// AST, so we cannot modify it.
1889+
if (!isa<clang::ClassTemplateSpecializationDecl>(named) &&
1890+
!tagDecl->getDefinition()) {
18851891
return;
1892+
}
18861893
}
1887-
18881894
// If we have a name to import as, add this entry to the table.
18891895
auto currentVersion =
18901896
ImportNameVersion::fromOptions(nameImporter.getLangOpts());
@@ -2077,6 +2083,19 @@ void SwiftLookupTableWriter::populateTableWithDecl(SwiftLookupTable &table,
20772083

20782084
// Add this entry to the lookup table.
20792085
addEntryToLookupTable(table, named, nameImporter);
2086+
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(named)) {
2087+
if (auto typedefType = dyn_cast<clang::TemplateSpecializationType>(
2088+
typedefDecl->getUnderlyingType())) {
2089+
if (auto CTSD = dyn_cast<clang::ClassTemplateSpecializationDecl>(
2090+
typedefType->getAsTagDecl())) {
2091+
// Adding template instantiation behind typedef as a top-level entry
2092+
// so the instantiation appears in the API.
2093+
assert(!isa<clang::ClassTemplatePartialSpecializationDecl>(CTSD) &&
2094+
"Class template partial specialization cannot appear behind typedef");
2095+
addEntryToLookupTable(table, CTSD, nameImporter);
2096+
}
2097+
}
2098+
}
20802099
}
20812100

20822101
void SwiftLookupTableWriter::populateTable(SwiftLookupTable &table,

test/Demangle/Inputs/manglings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,5 @@ $s0059xxxxxxxxxxxxxxx_ttttttttBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBee -> $s0059xxxx
365365
$sx1td_t ---> (t: A...)
366366
$s7example1fyyYF -> example.f() async -> ()
367367
$s7example1fyyYKF -> example.f() async throws -> ()
368+
$s4main20receiveInstantiationyySo34__CxxTemplateInst12MagicWrapperIiEVzF ---> main.receiveInstantiation(inout __C.__CxxTemplateInst12MagicWrapperIiE) -> ()
369+
$s4main19returnInstantiationSo34__CxxTemplateInst12MagicWrapperIiEVyF ---> main.returnInstantiation() -> __C.__CxxTemplateInst12MagicWrapperIiE
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_CANONICAL_TYPES_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_CANONICAL_TYPES_H
3+
4+
template<class T>
5+
struct MagicWrapper {
6+
T t;
7+
int getValuePlusArg(int arg) const { return t.getValue() + arg; }
8+
};
9+
10+
struct IntWrapper {
11+
int value;
12+
int getValue() const { return value; }
13+
};
14+
15+
typedef MagicWrapper<IntWrapper> WrappedMagicNumberA;
16+
typedef MagicWrapper<IntWrapper> WrappedMagicNumberB;
17+
18+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CANONICAL_TYPES_H
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_INCLUDING_MEMBERS_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_INCLUDING_MEMBERS_H
3+
4+
template<class T>
5+
struct MagicWrapper {
6+
T t;
7+
int getValuePlusArg(int arg) const { return t.getValue() + arg; }
8+
};
9+
10+
struct IntWrapper {
11+
int value;
12+
int getValue() const { return value; }
13+
};
14+
15+
inline int forceInstantiation() {
16+
auto t = MagicWrapper<IntWrapper>();
17+
return t.getValuePlusArg(14);
18+
}
19+
20+
// The ClassTemplateSpecializationDecl node for MagicWrapper<IntWrapper> already has a definition
21+
// because function above forced the instantiation. Its members are fully
22+
// instantiated, so nothing needs to be explicitly instantiated by the Swift
23+
// compiler.
24+
typedef MagicWrapper<IntWrapper> FullyDefinedMagicallyWrappedInt;
25+
26+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_INCLUDING_MEMBERS_H
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_H
3+
4+
template<class T>
5+
struct MagicWrapper {
6+
T t;
7+
int getValuePlusArg(int arg) const { return t.getValue() + arg; }
8+
};
9+
10+
struct IntWrapper {
11+
int value;
12+
int getValue() const { return value; }
13+
};
14+
15+
inline MagicWrapper<IntWrapper> forceInstantiation() {
16+
return MagicWrapper<IntWrapper>();
17+
}
18+
19+
// The ClassTemplateSpecializationDecl node for MagicWrapper<IntWrapper> already has a definition
20+
// because function above forced the instantiation. Its members are not
21+
// instantiated though, the Swift compiler needs to instantiate them.
22+
typedef MagicWrapper<IntWrapper> PartiallyDefinedMagicallyWrappedInt;
23+
24+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_H
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_PRIMITIVE_ARGUMENT_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_PRIMITIVE_ARGUMENT_H
3+
4+
template<class T>
5+
struct MagicWrapper {
6+
T t;
7+
int getValuePlusArg(int arg) const { return t + arg; }
8+
};
9+
10+
typedef MagicWrapper<int> WrappedMagicInt;
11+
12+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_PRIMITIVE_ARGUMENT_H
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITHOUT_DEFINITION_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITHOUT_DEFINITION_H
3+
4+
template<class T>
5+
struct MagicWrapper {
6+
T t;
7+
int getValuePlusArg(int arg) const { return t.getValue() + arg; }
8+
};
9+
10+
struct IntWrapper {
11+
int value;
12+
int getValue() const { return value; }
13+
};
14+
15+
// The ClassTemplateSpecializationDecl node for MagicWrapper<IntWrapper> doesn't have a
16+
// definition in Clang because nothing in this header required the
17+
// instantiation. Therefore, the Swift compiler must trigger instantiation.
18+
typedef MagicWrapper<IntWrapper> MagicallyWrappedIntWithoutDefinition;
19+
20+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITHOUT_DEFINITION_H

0 commit comments

Comments
 (0)