Skip to content

Commit 26e5742

Browse files
committed
[interop][SwiftToCxx] emit generic type traits for C++ types bridged into Swift and then back to C++
1 parent 0db1457 commit 26e5742

12 files changed

+154
-34
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#include "swift/AST/Decl.h"
1616
#include "swift/AST/Module.h"
1717
#include "swift/AST/SwiftNameTranslation.h"
18+
#include "clang/AST/ASTContext.h"
19+
#include "clang/AST/DeclTemplate.h"
20+
#include "clang/AST/NestedNameSpecifier.h"
1821

1922
using namespace swift;
2023
using namespace cxx_synthesis;
@@ -80,8 +83,35 @@ bool ClangSyntaxPrinter::printNominalTypeOutsideMemberDeclTemplateSpecifiers(
8083
return false;
8184
}
8285

86+
void ClangSyntaxPrinter::printNominalClangTypeReference(
87+
const clang::Decl *typeDecl) {
88+
auto &clangCtx = typeDecl->getASTContext();
89+
clang::PrintingPolicy pp(clangCtx.getLangOpts());
90+
const auto *NS = clang::NestedNameSpecifier::getRequiredQualification(
91+
clangCtx, clangCtx.getTranslationUnitDecl(),
92+
typeDecl->getLexicalDeclContext());
93+
if (NS)
94+
NS->print(os, pp);
95+
assert(cast<clang::NamedDecl>(typeDecl)->getDeclName().isIdentifier());
96+
os << cast<clang::NamedDecl>(typeDecl)->getName();
97+
if (auto *ctd = dyn_cast<clang::ClassTemplateSpecializationDecl>(typeDecl)) {
98+
if (ctd->getTemplateArgs().size()) {
99+
os << '<';
100+
llvm::interleaveComma(ctd->getTemplateArgs().asArray(), os,
101+
[&](const clang::TemplateArgument &arg) {
102+
arg.print(pp, os, /*IncludeType=*/true);
103+
});
104+
os << '>';
105+
}
106+
}
107+
}
108+
83109
void ClangSyntaxPrinter::printNominalTypeReference(
84110
const NominalTypeDecl *typeDecl, const ModuleDecl *moduleContext) {
111+
if (typeDecl->hasClangNode()) {
112+
printNominalClangTypeReference(typeDecl->getClangDecl());
113+
return;
114+
}
85115
printModuleNamespaceQualifiersIfNeeded(typeDecl->getModuleContext(),
86116
moduleContext);
87117
// FIXME: Full qualifiers for nested types?

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ class ClangSyntaxPrinter {
9090
void printNominalTypeReference(const NominalTypeDecl *typeDecl,
9191
const ModuleDecl *moduleContext);
9292

93+
/// Print out the C++ record qualifier for the given C++ record.
94+
void printNominalClangTypeReference(const clang::Decl *typeDecl);
95+
9396
/// Print out the C++ class access qualifier for the given Swift type
9497
/// declaration.
9598
///

lib/PrintAsClang/DeclAndTypePrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class DeclAndTypePrinter {
7272
requiresExposedAttribute(requiresExposedAttribute),
7373
outputLang(outputLang) {}
7474

75+
SwiftToClangInteropContext &getInteropContext() { return interopContext; }
76+
7577
/// Returns true if \p VD should be included in a compatibility header for
7678
/// the options the printer was constructed with.
7779
bool shouldInclude(const ValueDecl *VD);

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "PrimitiveTypeMapping.h"
1919
#include "PrintClangValueType.h"
2020
#include "PrintSwiftToClangCoreScaffold.h"
21+
#include "SwiftToClangInteropContext.h"
2122

2223
#include "swift/AST/ExistentialLayout.h"
2324
#include "swift/AST/Module.h"
@@ -677,8 +678,14 @@ class ModuleWriter {
677678
}
678679
printer.printAdHocCategory(make_range(groupBegin, delayedMembers.end()));
679680
}
681+
680682
// Print any out of line definitions.
681683
os << outOfLineDefinitionsOS.str();
684+
685+
// Print any additional metadata for referenced C++ types.
686+
for (const auto *typeDecl :
687+
printer.getInteropContext().getEmittedClangTypeDecls())
688+
ClangValueTypePrinter::printClangTypeSwiftGenericTraits(os, typeDecl, &M);
682689
}
683690
};
684691
} // end anonymous namespace

lib/PrintAsClang/PrintClangClassType.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ void ClangClassTypePrinter::printClassTypeDecl(
9595
});
9696

9797
ClangValueTypePrinter::printTypeGenericTraits(
98-
os, typeDecl, typeMetadataFuncName, /*genericRequirements=*/{});
98+
os, typeDecl, typeMetadataFuncName, /*genericRequirements=*/{},
99+
typeDecl->getModuleContext());
99100
}
100101

101102
void ClangClassTypePrinter::printClassTypeReturnScaffold(

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
#include "swift/ClangImporter/ClangImporter.h"
2929
#include "swift/IRGen/IRABIDetailsProvider.h"
3030
#include "clang/AST/ASTContext.h"
31-
#include "clang/AST/DeclTemplate.h"
32-
#include "clang/AST/NestedNameSpecifier.h"
3331
#include "llvm/ADT/STLExtras.h"
3432

3533
using namespace swift;
@@ -108,26 +106,7 @@ class ClangTypeHandler {
108106
}
109107

110108
void printTypeName(raw_ostream &os) const {
111-
auto &clangCtx = typeDecl->getASTContext();
112-
clang::PrintingPolicy pp(clangCtx.getLangOpts());
113-
const auto *NS = clang::NestedNameSpecifier::getRequiredQualification(
114-
clangCtx, clangCtx.getTranslationUnitDecl(),
115-
typeDecl->getLexicalDeclContext());
116-
if (NS)
117-
NS->print(os, pp);
118-
assert(cast<clang::NamedDecl>(typeDecl)->getDeclName().isIdentifier());
119-
os << cast<clang::NamedDecl>(typeDecl)->getName();
120-
if (auto *ctd =
121-
dyn_cast<clang::ClassTemplateSpecializationDecl>(typeDecl)) {
122-
if (ctd->getTemplateArgs().size()) {
123-
os << '<';
124-
llvm::interleaveComma(ctd->getTemplateArgs().asArray(), os,
125-
[&](const clang::TemplateArgument &arg) {
126-
arg.print(pp, os, /*IncludeType=*/true);
127-
});
128-
os << '>';
129-
}
130-
}
109+
ClangSyntaxPrinter(os).printNominalClangTypeReference(typeDecl);
131110
}
132111

133112
void printReturnScaffold(raw_ostream &os,
@@ -316,6 +295,7 @@ class CFunctionSignatureTypePrinter
316295
handler.printTypeName(os);
317296
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
318297
os << '&';
298+
interopContext.recordEmittedClangTypeDecl(decl);
319299
return ClangRepresentation::representable;
320300
}
321301

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
406406
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
407407

408408
printTypeGenericTraits(os, typeDecl, typeMetadataFuncName,
409-
typeMetadataFuncGenericParams);
409+
typeMetadataFuncGenericParams,
410+
typeDecl->getModuleContext());
410411
}
411412

412413
void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
@@ -456,24 +457,47 @@ void ClangValueTypePrinter::printValueTypeReturnScaffold(
456457
os << " });\n";
457458
}
458459

460+
void ClangValueTypePrinter::printClangTypeSwiftGenericTraits(
461+
raw_ostream &os, const NominalTypeDecl *typeDecl,
462+
const ModuleDecl *moduleContext) {
463+
assert(typeDecl->hasClangNode());
464+
// Do not reference unspecialized templates.
465+
if (isa<clang::ClassTemplateDecl>(typeDecl->getClangDecl()))
466+
return;
467+
auto typeMetadataFunc = irgen::LinkEntity::forTypeMetadataAccessFunction(
468+
typeDecl->getDeclaredType()->getCanonicalType());
469+
std::string typeMetadataFuncName = typeMetadataFunc.mangleAsString();
470+
printTypeGenericTraits(os, typeDecl, typeMetadataFuncName,
471+
/*typeMetadataFuncRequirements=*/{}, moduleContext);
472+
}
473+
459474
void ClangValueTypePrinter::printTypeGenericTraits(
460475
raw_ostream &os, const NominalTypeDecl *typeDecl,
461476
StringRef typeMetadataFuncName,
462-
ArrayRef<GenericRequirement> typeMetadataFuncRequirements) {
477+
ArrayRef<GenericRequirement> typeMetadataFuncRequirements,
478+
const ModuleDecl *moduleContext) {
463479
ClangSyntaxPrinter printer(os);
464480
// FIXME: avoid popping out of the module's namespace here.
465481
os << "} // end namespace \n\n";
466482
os << "namespace swift {\n";
467483

484+
if (typeDecl->hasClangNode()) {
485+
/// Print a reference to the type metadata fucntion for a C++ type.
486+
ClangSyntaxPrinter(os).printNamespace(
487+
cxx_synthesis::getCxxImplNamespaceName(), [&](raw_ostream &os) {
488+
ClangSyntaxPrinter(os).printCTypeMetadataTypeFunction(
489+
typeDecl, typeMetadataFuncName, typeMetadataFuncRequirements);
490+
});
491+
}
492+
468493
os << "#pragma clang diagnostic push\n";
469494
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
470495
if (typeMetadataFuncRequirements.empty()) {
471496
// FIXME: generic type support.
472497
os << "template<>\n";
473498
os << "static inline const constexpr bool isUsableInGenericContext<";
474-
printer.printBaseName(typeDecl->getModuleContext());
475-
os << "::";
476-
printer.printBaseName(typeDecl);
499+
printer.printNominalTypeReference(typeDecl,
500+
/*moduleContext=*/nullptr);
477501
os << "> = true;\n";
478502
}
479503
if (printer.printNominalTypeOutsideMemberDeclTemplateSpecifiers(typeDecl))
@@ -484,16 +508,27 @@ void ClangValueTypePrinter::printTypeGenericTraits(
484508
os << "> {\n";
485509
os << " static inline void * _Nonnull getTypeMetadata() {\n";
486510
os << " return ";
487-
printer.printBaseName(typeDecl->getModuleContext());
488-
os << "::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
511+
if (!typeDecl->hasClangNode()) {
512+
printer.printBaseName(typeDecl->getModuleContext());
513+
os << "::";
514+
}
515+
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
489516
ClangSyntaxPrinter(os).printSwiftTypeMetadataAccessFunctionCall(
490517
typeMetadataFuncName, typeMetadataFuncRequirements);
491518
os << "._0;\n";
492519
os << " }\n};\n";
493520

494521
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << "{\n";
495522

496-
if (!isa<ClassDecl>(typeDecl) && typeMetadataFuncRequirements.empty()) {
523+
if (typeDecl->hasClangNode()) {
524+
os << "template<>\n";
525+
os << "static inline const constexpr bool isSwiftBridgedCxxRecord<";
526+
printer.printNominalClangTypeReference(typeDecl->getClangDecl());
527+
os << "> = true;\n";
528+
}
529+
530+
if (!isa<ClassDecl>(typeDecl) && !typeDecl->hasClangNode() &&
531+
typeMetadataFuncRequirements.empty()) {
497532
// FIXME: generic support.
498533
os << "template<>\n";
499534
os << "static inline const constexpr bool isValueType<";
@@ -512,7 +547,7 @@ void ClangValueTypePrinter::printTypeGenericTraits(
512547
}
513548

514549
// FIXME: generic support.
515-
if (typeMetadataFuncRequirements.empty()) {
550+
if (!typeDecl->hasClangNode() && typeMetadataFuncRequirements.empty()) {
516551
os << "template<>\n";
517552
os << "struct implClassFor<";
518553
printer.printBaseName(typeDecl->getModuleContext());
@@ -528,6 +563,6 @@ void ClangValueTypePrinter::printTypeGenericTraits(
528563
os << "#pragma clang diagnostic pop\n";
529564
os << "} // namespace swift\n";
530565
os << "\nnamespace ";
531-
printer.printBaseName(typeDecl->getModuleContext());
566+
printer.printBaseName(moduleContext);
532567
os << " {\n";
533568
}

lib/PrintAsClang/PrintClangValueType.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,17 @@ class ClangValueTypePrinter {
9393
static void printTypeGenericTraits(
9494
raw_ostream &os, const NominalTypeDecl *typeDecl,
9595
StringRef typeMetadataFuncName,
96-
ArrayRef<GenericRequirement> typeMetadataFuncRequirements);
96+
ArrayRef<GenericRequirement> typeMetadataFuncRequirements,
97+
const ModuleDecl *moduleContext);
9798

9899
static void forwardDeclType(raw_ostream &os, const NominalTypeDecl *typeDecl);
99100

101+
/// Print out the type traits that allow a C++ type be used a Swift generic
102+
/// context.
103+
static void printClangTypeSwiftGenericTraits(raw_ostream &os,
104+
const NominalTypeDecl *typeDecl,
105+
const ModuleDecl *moduleContext);
106+
100107
private:
101108
raw_ostream &os;
102109
raw_ostream &cPrologueOS;

lib/PrintAsClang/SwiftToClangInteropContext.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "SwiftToClangInteropContext.h"
14+
#include "swift/AST/Decl.h"
1415
#include "swift/IRGen/IRABIDetailsProvider.h"
1516

1617
using namespace swift;
@@ -33,3 +34,9 @@ void SwiftToClangInteropContext::runIfStubForDeclNotEmitted(
3334
if (result.second)
3435
function();
3536
}
37+
38+
void SwiftToClangInteropContext::recordEmittedClangTypeDecl(
39+
const NominalTypeDecl *typeDecl) {
40+
assert(typeDecl->hasClangNode());
41+
referencedClangTypeDecls.insert(typeDecl);
42+
}

lib/PrintAsClang/SwiftToClangInteropContext.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#ifndef SWIFT_PRINTASCLANG_SWIFTTOCLANGINTEROPCONTEXT_H
1414
#define SWIFT_PRINTASCLANG_SWIFTTOCLANGINTEROPCONTEXT_H
1515

16+
#include "llvm/ADT/ArrayRef.h"
1617
#include "llvm/ADT/STLExtras.h"
18+
#include "llvm/ADT/SetVector.h"
1719
#include "llvm/ADT/StringSet.h"
1820
#include <memory>
1921

@@ -23,6 +25,7 @@ class Decl;
2325
class IRABIDetailsProvider;
2426
class IRGenOptions;
2527
class ModuleDecl;
28+
class NominalTypeDecl;
2629

2730
/// The \c SwiftToClangInteropContext class is responsible for providing
2831
/// access to the other required subsystems of the compiler during the emission
@@ -40,11 +43,21 @@ class SwiftToClangInteropContext {
4043
void runIfStubForDeclNotEmitted(llvm::StringRef stubName,
4144
llvm::function_ref<void(void)> function);
4245

46+
/// Records that the given nominal type decl that has a clang declaration was
47+
/// emitted in the generated header.
48+
void recordEmittedClangTypeDecl(const NominalTypeDecl *typeDecl);
49+
50+
inline const llvm::SetVector<const NominalTypeDecl *> &
51+
getEmittedClangTypeDecls() const {
52+
return referencedClangTypeDecls;
53+
}
54+
4355
private:
4456
ModuleDecl &mod;
4557
const IRGenOptions &irGenOpts;
4658
std::unique_ptr<IRABIDetailsProvider> irABIDetails;
4759
llvm::StringSet<> emittedStubs;
60+
llvm::SetVector<const NominalTypeDecl *> referencedClangTypeDecls;
4861
};
4962

5063
} // end namespace swift

stdlib/public/SwiftShims/_SwiftCxxInteroperability.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ template <class T> static inline const constexpr bool isValueType = false;
160160
/// boxed.
161161
template <class T> static inline const constexpr bool isOpaqueLayout = false;
162162

163+
/// True if the given type is a C++ record that was bridged to Swift, giving
164+
/// Swift ability to work with it in a generic context.
165+
template <class T>
166+
static inline const constexpr bool isSwiftBridgedCxxRecord = false;
167+
163168
/// Returns the opaque pointer to the given value.
164169
template <class T>
165170
inline const void *_Nonnull getOpaquePointer(const T &value) {

test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,33 @@ public func takeTrivialInout(_ x: inout Trivial) {
140140
// CHECK: inline void takeTrivialInout(Trivial& x) noexcept {
141141
// CHECK-NEXT: return _impl::$s8UseCxxTy16takeTrivialInoutyySo0E0VzF(swift::_impl::getOpaquePointer(x));
142142
// CHECK-NEXT: }
143+
144+
// CHECK: } // end namespace
145+
// CHECK-EMPTY:
146+
// CHECK-NEXT: namespace swift {
147+
// CHECK-NEXT: namespace _impl {
148+
// CHECK-EMPTY:
149+
// CHECK-NEXT: // Type metadata accessor for __CxxTemplateInstN2ns18NonTrivialTemplateIiEE
150+
// CHECK-NEXT: SWIFT_EXTERN swift::_impl::MetadataResponseTy $sSo2nsO033__CxxTemplateInstN2ns18NonTrivialC4IiEEVMa(swift::_impl::MetadataRequestTy) SWIFT_NOEXCEPT SWIFT_CALL;
151+
// CHECK-EMPTY:
152+
// CHECK-EMPTY:
153+
// CHECK-NEXT: } // namespace _impl
154+
// CHECK-EMPTY:
155+
// CHECK-NEXT: #pragma clang diagnostic push
156+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
157+
// CHECK-NEXT: template<>
158+
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<ns::NonTrivialTemplate<int>> = true;
159+
// CHECK-NEXT: template<>
160+
// CHECK-NEXT: struct TypeMetadataTrait<ns::NonTrivialTemplate<int>> {
161+
// CHECK-NEXT: static inline void * _Nonnull getTypeMetadata() {
162+
// CHECK-NEXT: return _impl::$sSo2nsO033__CxxTemplateInstN2ns18NonTrivialC4IiEEVMa(0)._0;
163+
// CHECK-NEXT: }
164+
// CHECK-NEXT: };
165+
// CHECK-NEXT: namespace _impl{
166+
// CHECK-NEXT: template<>
167+
// CHECK-NEXT: static inline const constexpr bool isSwiftBridgedCxxRecord<ns::NonTrivialTemplate<int>> = true;
168+
// CHECK-NEXT: } // namespace
169+
// CHECK-NEXT: #pragma clang diagnostic pop
170+
// CHECK-NEXT: } // namespace swift
171+
// CHECK-EMPTY:
172+
// CHECK-NEXT: namespace UseCxxTy {

0 commit comments

Comments
 (0)