Skip to content

Commit 2a05109

Browse files
committed
[interop][SwiftToCxx] Declare the generic usability type trait before the C++ class that represents the Swift type
This allows you to import a method that returns the type of the context in which the method is declared when such type is a generic parameter in another type. This means that it's now possible to bridge the initializer for RawRepresentable enums.
1 parent 2132cc9 commit 2a05109

15 files changed

+160
-27
lines changed

Diff for: lib/PrintAsClang/DeclAndTypePrinter.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ class DeclAndTypePrinter::Implementation
297297
if (outputLang == OutputLanguageMode::Cxx) {
298298
// FIXME: Non objc class.
299299
// FIXME: Print availability.
300+
// FIXME: forward decl should be handled by ModuleWriter.
301+
ClangValueTypePrinter::forwardDeclType(os, CD);
300302
ClangClassTypePrinter(os).printClassTypeDecl(
301303
CD, [&]() { printMembers(CD->getMembers()); });
302304
return;

Diff for: lib/PrintAsClang/ModuleContentsWriter.cpp

+11-7
Original file line numberDiff line numberDiff line change
@@ -274,17 +274,19 @@ class ModuleWriter {
274274
ClangValueTypePrinter::printClangTypeSwiftGenericTraits(os, typeDecl, &M);
275275
}
276276

277+
void forwardDeclareCxxValueTypeIfNeeded(const NominalTypeDecl *NTD) {
278+
forwardDeclare(NTD,
279+
[&]() { ClangValueTypePrinter::forwardDeclType(os, NTD); });
280+
}
281+
277282
void forwardDeclareType(const TypeDecl *TD) {
278283
if (outputLangMode == OutputLanguageMode::Cxx) {
279284
if (isa<StructDecl>(TD) || isa<EnumDecl>(TD)) {
280285
auto *NTD = cast<NominalTypeDecl>(TD);
281-
if (!addImport(NTD)) {
282-
forwardDeclare(
283-
NTD, [&]() { ClangValueTypePrinter::forwardDeclType(os, NTD); });
284-
} else {
285-
if (isa<StructDecl>(TD) && NTD->hasClangNode())
286-
emitReferencedClangTypeMetadata(NTD);
287-
}
286+
if (!addImport(NTD))
287+
forwardDeclareCxxValueTypeIfNeeded(NTD);
288+
else if (isa<StructDecl>(TD) && NTD->hasClangNode())
289+
emitReferencedClangTypeMetadata(NTD);
288290
}
289291
return;
290292
}
@@ -471,6 +473,7 @@ class ModuleWriter {
471473
printer.getInteropContext().getExtensionsForNominalType(SD)) {
472474
(void)forwardDeclareMemberTypes(ed->getMembers(), SD);
473475
}
476+
forwardDeclareCxxValueTypeIfNeeded(SD);
474477
}
475478
printer.print(SD);
476479
return true;
@@ -536,6 +539,7 @@ class ModuleWriter {
536539

537540
if (outputLangMode == OutputLanguageMode::Cxx) {
538541
forwardDeclareMemberTypes(ED->getMembers(), ED);
542+
forwardDeclareCxxValueTypeIfNeeded(ED);
539543
}
540544

541545
if (seenTypes[ED].first == EmissionState::Defined)

Diff for: lib/PrintAsClang/PrintClangValueType.cpp

+29-2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ void ClangValueTypePrinter::forwardDeclType(raw_ostream &os,
9191
os << "class ";
9292
ClangSyntaxPrinter(os).printBaseName(typeDecl);
9393
os << ";\n";
94+
printTypePrecedingGenericTraits(os, typeDecl, typeDecl->getModuleContext());
9495
}
9596

9697
static void addCppExtensionsToStdlibType(const NominalTypeDecl *typeDecl,
@@ -471,6 +472,32 @@ void ClangValueTypePrinter::printClangTypeSwiftGenericTraits(
471472
/*typeMetadataFuncRequirements=*/{}, moduleContext);
472473
}
473474

475+
void ClangValueTypePrinter::printTypePrecedingGenericTraits(
476+
raw_ostream &os, const NominalTypeDecl *typeDecl,
477+
const ModuleDecl *moduleContext) {
478+
assert(!typeDecl->hasClangNode());
479+
ClangSyntaxPrinter printer(os);
480+
// FIXME: avoid popping out of the module's namespace here.
481+
os << "} // end namespace \n\n";
482+
os << "namespace swift {\n";
483+
484+
os << "#pragma clang diagnostic push\n";
485+
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
486+
if (!typeDecl->isGeneric()) {
487+
// FIXME: generic type support.
488+
os << "template<>\n";
489+
os << "static inline const constexpr bool isUsableInGenericContext<";
490+
printer.printNominalTypeReference(typeDecl,
491+
/*moduleContext=*/nullptr);
492+
os << "> = true;\n";
493+
}
494+
os << "#pragma clang diagnostic pop\n";
495+
os << "} // namespace swift\n";
496+
os << "\nnamespace ";
497+
printer.printBaseName(moduleContext);
498+
os << " {\n";
499+
}
500+
474501
void ClangValueTypePrinter::printTypeGenericTraits(
475502
raw_ostream &os, const NominalTypeDecl *typeDecl,
476503
StringRef typeMetadataFuncName,
@@ -492,8 +519,8 @@ void ClangValueTypePrinter::printTypeGenericTraits(
492519

493520
os << "#pragma clang diagnostic push\n";
494521
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
495-
if (typeMetadataFuncRequirements.empty()) {
496-
// FIXME: generic type support.
522+
if (typeDecl->hasClangNode()) {
523+
// FIXME: share the code.
497524
os << "template<>\n";
498525
os << "static inline const constexpr bool isUsableInGenericContext<";
499526
printer.printNominalTypeReference(typeDecl,

Diff for: lib/PrintAsClang/PrintClangValueType.h

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ class ClangValueTypePrinter {
9696
ArrayRef<GenericRequirement> typeMetadataFuncRequirements,
9797
const ModuleDecl *moduleContext);
9898

99+
static void printTypePrecedingGenericTraits(raw_ostream &os,
100+
const NominalTypeDecl *typeDecl,
101+
const ModuleDecl *moduleContext);
102+
99103
static void forwardDeclType(raw_ostream &os, const NominalTypeDecl *typeDecl);
100104

101105
/// Print out the type traits that allow a C++ type be used a Swift generic

Diff for: test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift

+17-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,23 @@ public final class ClassWithIntField {
3030

3131
// CHECK: namespace Class {
3232

33-
// CHECK: namespace _impl {
33+
// CHECK: class ClassWithIntField;
34+
// CHECK-NEXT: } // end namespace
35+
36+
// CHECK: namespace
37+
// CHECK-SAME: swift {
38+
// CHECK-NEXT: #pragma clang diagnostic push
39+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
40+
// CHECK-NEXT: template<>
41+
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
42+
// CHECK-NEXT: #pragma clang diagnostic pop
43+
// CHECK-NEXT: } // namespace swift
44+
45+
// CHECK: namespace
46+
// CHECK-SAME: Class {
47+
48+
// CHECK: namespace
49+
// CHECK-SAME: _impl {
3450
// CHECK-EMPTY:
3551
// CHECK-NEXT: class _impl_ClassWithIntField;
3652
// CHECK-NEXT: // Type metadata accessor for ClassWithIntField
@@ -64,8 +80,6 @@ public final class ClassWithIntField {
6480
// CHECK-NEXT: #pragma clang diagnostic push
6581
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
6682
// CHECK-NEXT: template<>
67-
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
68-
// CHECK-NEXT: template<>
6983
// CHECK-NEXT: struct TypeMetadataTrait<Class::ClassWithIntField> {
7084
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
7185
// CHECK-NEXT: return Class::_impl::$s5Class0A12WithIntFieldCMa(0)._0;

Diff for: test/Interop/SwiftToCxx/enums/zero-sized-enum-in-cxx.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public enum EmptyEnum {}
66
public enum SingleCaseEnum { case first }
77

88
// CHECK: namespace Enums {
9-
// CHECK-NOT: EmptyEnum
10-
// CHECK-NOT: SingleCaseEnum
9+
// CHECK-NOT: class EmptyEnum final {
10+
// CHECK-NOT: class SingleCaseEnum final {
1111
// CHECK: } // namespace Enums

Diff for: test/Interop/SwiftToCxx/generics/generic-enum-in-cxx.swift

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
8989
}
9090
}
9191

92+
// CHECK: template<class T_0_0>
93+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
94+
// CHECK-NEXT: class GenericOpt;
95+
9296
// CHECK: template<class T_0_0>
9397
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
9498
// CHECK-NEXT: class _impl_GenericOpt;

Diff for: test/Interop/SwiftToCxx/generics/generic-struct-in-cxx.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ public func inoutConcretePair(_ x: UInt16, _ y: inout GenericPair<UInt16, UInt16
200200
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics16takeConcretePairyyAA07GenericD0Vys6UInt16VAFGF(struct swift_interop_passStub_Generics_uint32_t_0_4 x) SWIFT_NOEXCEPT SWIFT_CALL; // takeConcretePair(_:)
201201
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics15takeGenericPairyyAA0cD0Vyxq_Gr0_lF(const void * _Nonnull x, void * _Nonnull , void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // takeGenericPair(_:)
202202

203+
// CHECK: template<class T_0_0, class T_0_1>
204+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
205+
// CHECK-NEXT: class GenericPair;
206+
203207
// CHECK: template<class T_0_0, class T_0_1>
204208
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
205209
// CHECK-NEXT: class _impl_GenericPair;
@@ -247,11 +251,7 @@ public func inoutConcretePair(_ x: UInt16, _ y: inout GenericPair<UInt16, UInt16
247251
// CHECK-NEXT: #pragma clang diagnostic pop
248252
// CHECK-NEXT: } // namespace swift
249253

250-
// CHECK: template<class T_0_0, class T_0_1>
251-
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
252-
// CHECK-NEXT: class GenericPair;
253-
// CHECK-EMPTY:
254-
// CHECK-NEXT: inline void inoutConcretePair(uint16_t x, GenericPair<uint16_t, uint16_t>& y) noexcept {
254+
// CHECK: inline void inoutConcretePair(uint16_t x, GenericPair<uint16_t, uint16_t>& y) noexcept {
255255
// CHECK-NEXT: return _impl::$s8Generics17inoutConcretePairyys6UInt16V_AA07GenericD0VyA2DGztF(x, _impl::_impl_GenericPair<uint16_t, uint16_t>::getOpaquePointer(y));
256256
// CHECK-NEXT: }
257257

Diff for: test/Interop/SwiftToCxx/generics/generic-struct-known-layout-direct-in-cxx.swift

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@
7070
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics16takeConcretePairyyAA07GenericD0Vys6UInt16VAFGF(struct swift_interop_passStub_Generics_[[PTRPTRENC]] x) SWIFT_NOEXCEPT SWIFT_CALL; // takeConcretePair(_:)
7171
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics15takeGenericPairyyAA0cD0Vyxq_Gr0_lF(struct swift_interop_passStub_Generics_[[PTRPTRENC]] x, void * _Nonnull , void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // takeGenericPair(_:)
7272

73+
// CHECK: template<class T_0_0, class T_0_1>
74+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
75+
// CHECK-NEXT: class GenericPair;
7376

7477
// CHECK: template<class T_0_0, class T_0_1>
7578
// CHECK: template<class T_0_0, class T_0_1>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Generics -enable-experimental-cxx-interop -emit-clang-header-path %t/generics.h
3+
// RUN: %FileCheck %s < %t/generics.h
4+
// RUN: %check-generic-interop-cxx-header-in-clang(%t/generics.h)
5+
6+
@_expose(Cxx)
7+
public enum ComesFirstEnum {
8+
case A
9+
case B
10+
11+
public func returnsLaterOpt() -> LaterGeneric<ComesFirstEnum> { return LaterGeneric(x: ComesFirstEnum.A) }
12+
13+
public var first: ComesFirstStruct {
14+
return ComesFirstStruct(x: 42)
15+
}
16+
}
17+
18+
@_expose(Cxx)
19+
public struct ComesFirstStruct {
20+
let x: Int
21+
22+
public func returnsLaterOpt() -> LaterGeneric<ComesFirstStruct> { return LaterGeneric(x: ComesFirstStruct(x: 0)) }
23+
}
24+
25+
@_expose(Cxx)
26+
public struct LaterGeneric<T> {
27+
let x: T
28+
}
29+
30+
31+
// CHECK: class LaterGeneric;
32+
33+
// CHECK: class ComesFirstStruct;
34+
// CHECK: static inline const constexpr bool isUsableInGenericContext<Generics::ComesFirstStruct> = true;
35+
36+
// CHECK: class ComesFirstEnum;
37+
// CHECK: static inline const constexpr bool isUsableInGenericContext<Generics::ComesFirstEnum> = true;
38+
39+
// CHECK: class ComesFirstEnum final {
40+
// CHECK: LaterGeneric<ComesFirstEnum> returnsLaterOpt() const;
41+
42+
// CHECK: namespace Generics {
43+
// CHECK-EMPTY:
44+
// CHECK-NEXT: namespace _impl {
45+
// CHECK-EMPTY:
46+
// CHECK-NEXT: class _impl_ComesFirstStruct;
47+
48+
// CHECK: class ComesFirstStruct final {
49+
// CHECK: LaterGeneric<ComesFirstStruct> returnsLaterOpt() const;
50+
// CHECK: class LaterGeneric final {

Diff for: test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift

+7
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88

99
// CHECK: namespace Swift {
1010

11+
// CHECK: template<class T_0_0>
12+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
13+
// CHECK-NEXT: class Optional;
14+
1115
// CHECK: class String;
1216

17+
// CHECK: template<class T_0_0>
18+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
19+
// CHECK-NEXT: class Array;
1320
// CHECK: template<class T_0_0>
1421
// CHECK: template<class T_0_0>
1522
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>

Diff for: test/Interop/SwiftToCxx/structs/resilient-struct-in-cxx.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public struct FirstSmallStruct {
2727
}
2828
}
2929

30+
// CHECK: class FirstSmallStruct;
31+
32+
// CHECK: template<>
33+
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::FirstSmallStruct> = true;
34+
3035
// CHECK: class FirstSmallStruct final {
3136
// CHECK-NEXT: public:
3237
// CHECK: inline FirstSmallStruct(const FirstSmallStruct &other) {
@@ -71,8 +76,6 @@ public struct FirstSmallStruct {
7176
// CHECK-NEXT: #pragma clang diagnostic push
7277
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
7378
// CHECK-NEXT: template<>
74-
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::FirstSmallStruct> = true;
75-
// CHECK-NEXT: template<>
7679
// CHECK-NEXT: struct TypeMetadataTrait<Structs::FirstSmallStruct> {
7780
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
7881
// CHECK-NEXT: return Structs::_impl::$s7Structs16FirstSmallStructVMa(0)._0;

Diff for: test/Interop/SwiftToCxx/structs/swift-struct-circular-dependent-defs.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ public struct B {
2525
}
2626

2727
// CHECK: class B;
28-
// CHECK-NEXT: namespace _impl {
28+
// CHECK: class A;
29+
// CHECK: namespace _impl {
2930
// CHECK-EMPTY:
3031
// CHECK-NEXT: class _impl_A;
3132

3233
// CHECK: class A final {
3334

3435
// CHECK: B returnsB() const;
3536

36-
// CHECK: class A;
37-
// CHECK-NEXT: namespace _impl {
37+
// CHECK: namespace _impl {
38+
// CHECK-EMPTY:
39+
// CHECK-NEXT: class _impl_A {
40+
41+
// CHECK: namespace _impl {
3842
// CHECK-EMPTY:
3943
// CHECK-NEXT: class _impl_B;
4044

Diff for: test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@
99

1010
// CHECK: namespace Structs {
1111

12+
// CHECK: class StructWithIntField;
13+
// CHECK-NEXT: } // end namespace
14+
15+
// CHECK: namespace swift {
16+
// CHECK-NEXT: #pragma clang diagnostic push
17+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
18+
// CHECK-NEXT: template<>
19+
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::StructWithIntField> = true;
20+
// CHECK-NEXT: #pragma clang diagnostic pop
21+
// CHECK-NEXT: } // namespace swift
22+
23+
// CHECK: namespace Structs {
24+
1225
// CHECK: namespace _impl {
1326
// CHECK-EMPTY:
1427
// CHECK-NEXT: class _impl_StructWithIntField;
@@ -68,8 +81,6 @@
6881
// CHECK-NEXT: #pragma clang diagnostic push
6982
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
7083
// CHECK-NEXT: template<>
71-
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::StructWithIntField> = true;
72-
// CHECK-NEXT: template<>
7384
// CHECK-NEXT: struct TypeMetadataTrait<Structs::StructWithIntField>
7485
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
7586
// CHECK-NEXT: return Structs::_impl::$s7Structs18StructWithIntFieldVMa(0)._0;

Diff for: test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
// CHECK: namespace Structs {
66

7-
// CHECK-NOT: ZeroSizedStruct
7+
// CHECK-NOT: class ZeroSizedStruct final {
88

99
public struct ZeroSizedStruct {}
1010

0 commit comments

Comments
 (0)