Skip to content

Commit 143ce1e

Browse files
committed
[interop][SwiftToCxx] bridge Swift's Optional type in an experimental manner
1 parent b96bcb4 commit 143ce1e

File tree

5 files changed

+285
-23
lines changed

5 files changed

+285
-23
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,8 @@ static bool hasExposeAttr(const ValueDecl *VD, bool isExtension = false) {
25712571
return true;
25722572
if (VD == VD->getASTContext().getArrayDecl())
25732573
return true;
2574+
if (VD == VD->getASTContext().getOptionalDecl() && !isExtension)
2575+
return true;
25742576
return false;
25752577
}
25762578
if (VD->getAttrs().hasAttribute<ExposeAttr>())

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,34 @@ class CFunctionSignatureTypePrinter
186186
auto knownTypeInfo = getKnownTypeInfo(typeDecl, typeMapping, languageMode);
187187
if (!knownTypeInfo)
188188
return false;
189-
os << knownTypeInfo->name;
190-
if (knownTypeInfo->canBeNullable) {
191-
printNullability(optionalKind);
192-
}
189+
bool shouldPrintOptional = optionalKind && *optionalKind != OTK_None &&
190+
!knownTypeInfo->canBeNullable;
191+
if (!isInOutParam && shouldPrintOptional &&
192+
typeUseKind == FunctionSignatureTypeUse::ParamType)
193+
os << "const ";
194+
printOptional(shouldPrintOptional ? optionalKind : llvm::None, [&]() {
195+
os << knownTypeInfo->name;
196+
if (knownTypeInfo->canBeNullable) {
197+
printNullability(optionalKind);
198+
}
199+
});
200+
if (!isInOutParam && shouldPrintOptional &&
201+
typeUseKind == FunctionSignatureTypeUse::ParamType)
202+
os << '&';
193203
if (isInOutParam)
194204
printInoutTypeModifier();
195205
return true;
196206
}
197207

208+
void printOptional(Optional<OptionalTypeKind> optionalKind,
209+
llvm::function_ref<void()> body) {
210+
if (!optionalKind || optionalKind == OTK_None)
211+
return body();
212+
os << "Swift::Optional<";
213+
body();
214+
os << '>';
215+
}
216+
198217
ClangRepresentation visitType(TypeBase *Ty,
199218
Optional<OptionalTypeKind> optionalKind,
200219
bool isInOutParam) {
@@ -241,14 +260,18 @@ class CFunctionSignatureTypePrinter
241260
bool isInOutParam) {
242261
// FIXME: handle optionalKind.
243262
if (languageMode != OutputLanguageMode::Cxx) {
244-
os << "void * _Nonnull";
263+
os << "void * "
264+
<< (!optionalKind || *optionalKind == OTK_None ? "_Nonnull"
265+
: "_Nullable");
245266
if (isInOutParam)
246267
os << " * _Nonnull";
247268
return ClangRepresentation::representable;
248269
}
249270
if (typeUseKind == FunctionSignatureTypeUse::ParamType && !isInOutParam)
250271
os << "const ";
251-
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
272+
printOptional(optionalKind, [&]() {
273+
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
274+
});
252275
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
253276
os << "&";
254277
return ClangRepresentation::representable;
@@ -298,9 +321,6 @@ class CFunctionSignatureTypePrinter
298321
if (!declPrinter.shouldInclude(decl))
299322
return ClangRepresentation::unsupported; // FIXME: propagate why it's not
300323
// exposed.
301-
// FIXME: Support Optional<T>.
302-
if (optionalKind && *optionalKind != OTK_None)
303-
return ClangRepresentation::unsupported;
304324
// Only C++ mode supports struct types.
305325
if (languageMode != OutputLanguageMode::Cxx)
306326
return ClangRepresentation::unsupported;
@@ -312,22 +332,27 @@ class CFunctionSignatureTypePrinter
312332
if (typeUseKind == FunctionSignatureTypeUse::ParamType &&
313333
!isInOutParam)
314334
os << "const ";
315-
handler.printTypeName(os);
335+
printOptional(optionalKind, [&]() { handler.printTypeName(os); });
316336
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
317337
os << '&';
318338
return ClangRepresentation::representable;
319339
}
320340

321-
// FIXME: Handle optional structures.
322341
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
323342
if (!isInOutParam) {
324343
os << "const ";
325344
}
326-
ClangSyntaxPrinter(os).printPrimaryCxxTypeName(decl, moduleContext);
327-
auto result = visitGenericArgs(genericArgs);
345+
ClangRepresentation result = ClangRepresentation::representable;
346+
printOptional(optionalKind, [&]() {
347+
ClangSyntaxPrinter(os).printPrimaryCxxTypeName(decl, moduleContext);
348+
result = visitGenericArgs(genericArgs);
349+
});
328350
os << '&';
329351
return result;
330-
} else {
352+
}
353+
354+
ClangRepresentation result = ClangRepresentation::representable;
355+
printOptional(optionalKind, [&]() {
331356
ClangValueTypePrinter printer(os, cPrologueOS, interopContext);
332357
printer.printValueTypeReturnType(
333358
decl, languageMode,
@@ -336,9 +361,9 @@ class CFunctionSignatureTypePrinter
336361
ClangValueTypePrinter::TypeUseKind::CxxTypeName)
337362
: ClangValueTypePrinter::TypeUseKind::CxxTypeName,
338363
moduleContext);
339-
return visitGenericArgs(genericArgs);
340-
}
341-
return ClangRepresentation::representable;
364+
result = visitGenericArgs(genericArgs);
365+
});
366+
return result;
342367
}
343368

344369
Optional<ClangRepresentation>
@@ -396,13 +421,10 @@ class CFunctionSignatureTypePrinter
396421
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
397422
Optional<OptionalTypeKind> optionalKind,
398423
bool isInOutParam) {
399-
// FIXME: Support Optional<T>.
400-
if (optionalKind && *optionalKind != OTK_None)
401-
return ClangRepresentation::unsupported;
402424
bool isParam = typeUseKind == FunctionSignatureTypeUse::ParamType;
403425
if (isParam && !isInOutParam)
404426
os << "const ";
405-
// FIXME: handle optionalKind.
427+
406428
if (languageMode != OutputLanguageMode::Cxx) {
407429
// Note: This can happen for UnsafeMutablePointer<T>.
408430
if (typeUseKind != FunctionSignatureTypeUse::ParamType)
@@ -412,7 +434,9 @@ class CFunctionSignatureTypePrinter
412434
os << "void * _Nonnull";
413435
return ClangRepresentation::representable;
414436
}
415-
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(genericTpt);
437+
printOptional(optionalKind, [&]() {
438+
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(genericTpt);
439+
});
416440
// Pass a reference to the template type.
417441
if (isParam)
418442
os << '&';
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies
5+
6+
// RUN: %target-swift-frontend -typecheck %t/use-optional.swift -typecheck -module-name UseOptional -enable-experimental-cxx-interop -emit-clang-header-path %t/UseOptional.h
7+
8+
// RUN: %target-interop-build-clangxx -fno-exceptions -std=gnu++20 -c %t/optional-execution.cpp -I %t -o %t/swift-stdlib-execution.o
9+
// RUN: %target-build-swift %t/use-optional.swift -o %t/swift-stdlib-execution -Xlinker %t/swift-stdlib-execution.o -module-name UseOptional -Xfrontend -entry-point-function-name -Xfrontend swiftMain
10+
// RUN: %target-codesign %t/swift-stdlib-execution
11+
// RUN: %target-run %t/swift-stdlib-execution | %FileCheck %s
12+
13+
// REQUIRES: executable_test
14+
15+
//--- use-optional.swift
16+
17+
@_expose(Cxx)
18+
public struct SmallStruct {
19+
public let x: Int16
20+
}
21+
22+
@_expose(Cxx)
23+
public class Klass {
24+
public let x: Int16
25+
26+
init(_ x: Int16) {
27+
self.x = x
28+
print("init-Klass")
29+
}
30+
31+
deinit {
32+
print("deinit-Klass")
33+
}
34+
}
35+
36+
@_expose(Cxx)
37+
public func createCIntOpt(_ val: CInt) -> Optional<CInt> {
38+
return val
39+
}
40+
41+
@_expose(Cxx)
42+
public func takeCIntOpt(_ val: Optional<CInt>) {
43+
print(String(describing: val))
44+
}
45+
46+
@_expose(Cxx)
47+
public func createSmallStructOpt(_ val: Int16) -> Optional<SmallStruct> {
48+
return SmallStruct(x: val)
49+
}
50+
51+
@_expose(Cxx)
52+
public func takeSmallStructOpt(_ val: Optional<SmallStruct>) {
53+
print(String(describing: val))
54+
}
55+
56+
@_expose(Cxx)
57+
public func createKlassOpt(_ val: Int16) -> Klass? {
58+
return Klass(val)
59+
}
60+
61+
@_expose(Cxx)
62+
public func takeKlassOpt(_ val: Klass?) {
63+
print(String(describing: val))
64+
}
65+
66+
@_expose(Cxx)
67+
public func resetOpt<T>(_ val: inout Optional<T>) {
68+
val = .none
69+
}
70+
71+
//--- optional-execution.cpp
72+
73+
#include <cassert>
74+
#include "Swift.h"
75+
#include "UseOptional.h"
76+
77+
int main() {
78+
using namespace Swift;
79+
using namespace UseOptional;
80+
81+
{
82+
auto val = createCIntOpt(2);
83+
takeCIntOpt(val);
84+
assert(val.isSome());
85+
assert(val.getUnsafelyUnwrapped() == 2);
86+
resetOpt(val);
87+
assert(val.isNone());
88+
takeCIntOpt(val);
89+
}
90+
// CHECK: Optional(2)
91+
// CHECK-NEXT: nil
92+
{
93+
auto val = createSmallStructOpt(0xFA);
94+
takeSmallStructOpt(val);
95+
assert(val.isSome());
96+
assert(val.getUnsafelyUnwrapped().getX() == 0xFA);
97+
resetOpt(val);
98+
assert(val.isNone());
99+
takeSmallStructOpt(val);
100+
}
101+
// CHECK-NEXT: Optional(UseOptional.SmallStruct(x: 250))
102+
// CHECK-NEXT: nil
103+
{
104+
auto val = createKlassOpt(-256);
105+
takeKlassOpt(val);
106+
assert(val.isSome());
107+
auto ptr = val.getUnsafelyUnwrapped();
108+
assert(ptr.getX() == -256);
109+
resetOpt(val);
110+
assert(val.isNone());
111+
takeKlassOpt(val);
112+
}
113+
// CHECK-NEXT: init-Klass
114+
// CHECK-NEXT: Optional(UseOptional.Klass)
115+
// CHECK-NEXT: nil
116+
// CHECK-NEXT: deinit-Klass
117+
return 0;
118+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -typecheck %s -typecheck -module-name UseOptional -enable-experimental-cxx-interop -emit-clang-header-path %t/useopt.h
4+
5+
// RUN: %FileCheck %s < %t/useopt.h
6+
7+
@_expose(Cxx)
8+
public struct SmallStruct {
9+
let x: Int16
10+
}
11+
12+
@_expose(Cxx)
13+
public class Klass {
14+
let x: Int16
15+
16+
init(_ x: Int16) {
17+
self.x = x
18+
}
19+
}
20+
21+
@_expose(Cxx)
22+
public func createCIntOpt(_ val: CInt) -> Optional<CInt> {
23+
return val
24+
}
25+
26+
@_expose(Cxx)
27+
public func takeCIntOpt(_ val: Optional<CInt>) {
28+
print(String(describing: val))
29+
}
30+
31+
@_expose(Cxx)
32+
public func createSmallStructOpt(_ val: Int16) -> SmallStruct? {
33+
return SmallStruct(x: val)
34+
}
35+
36+
@_expose(Cxx)
37+
public func takeSmallStructOpt(_ val: Optional<SmallStruct>) {
38+
print(String(describing: val))
39+
}
40+
41+
@_expose(Cxx)
42+
public func createKlassOpt(_ val: Int16) -> Klass? {
43+
return Klass(val)
44+
}
45+
46+
@_expose(Cxx)
47+
public func takeKlassOpt(_ val: Klass?) {
48+
print(String(describing: val))
49+
}
50+
51+
@_expose(Cxx)
52+
public func resetOpt<T>(_ val: inout Optional<T>) {
53+
val = .none
54+
}
55+
56+
57+
// CHECK: inline Swift::Optional<int> createCIntOpt(int val) noexcept SWIFT_WARN_UNUSED_RESULT {
58+
// CHECK-NEXT: return Swift::_impl::_impl_Optional<int>::returnNewValue([&](char * _Nonnull result) {
59+
// CHECK-NEXT: _impl::swift_interop_returnDirect_UseOptional_[[CINTENC:[a-z0-9_]+]](result, _impl::$s11UseOptional13createCIntOptys5Int32VSgADF(val));
60+
// CHECK-NEXT: });
61+
// CHECK-NEXT: }
62+
63+
64+
// CHECK: inline Swift::Optional<Klass> createKlassOpt(int16_t val) noexcept SWIFT_WARN_UNUSED_RESULT {
65+
// CHECK-NEXT: return Swift::_impl::_impl_Optional<Klass>::returnNewValue([&](char * _Nonnull result) {
66+
// CHECK-NEXT: _impl::swift_interop_returnDirect_UseOptional_[[CLASSENC:[a-z0-9_]+]](result, _impl::$s11UseOptional14createKlassOptyAA0D0CSgs5Int16VF(val));
67+
// CHECK-NEXT: });
68+
// CHECK-NEXT: }
69+
70+
// CHECK: inline Swift::Optional<SmallStruct> createSmallStructOpt(int16_t val) noexcept SWIFT_WARN_UNUSED_RESULT {
71+
// CHECK-NEXT: return Swift::_impl::_impl_Optional<SmallStruct>::returnNewValue([&](char * _Nonnull result) {
72+
// CHECK-NEXT: _impl::swift_interop_returnDirect_UseOptional_uint32_t_0_4(result, _impl::$s11UseOptional20createSmallStructOptyAA0dE0VSgs5Int16VF(val));
73+
// CHECK-NEXT: });
74+
// CHECK-NEXT: }
75+
76+
77+
// CHECK: template<class T_0_0>
78+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
79+
// CHECK-NEXT: inline void resetOpt(Swift::Optional<T_0_0>& val) noexcept {
80+
// CHECK-NEXT: return _impl::$s11UseOptional8resetOptyyxSgzlF(Swift::_impl::_impl_Optional<T_0_0>::getOpaquePointer(val), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
81+
// CHECK-NEXT: }
82+
83+
84+
// CHECK: inline void takeCIntOpt(const Swift::Optional<int>& val) noexcept {
85+
// CHECK-NEXT: return _impl::$s11UseOptional11takeCIntOptyys5Int32VSgF(_impl::swift_interop_passDirect_UseOptional_[[CINTENC]](Swift::_impl::_impl_Optional<int>::getOpaquePointer(val)));
86+
// CHECK-NEXT: }
87+
88+
89+
// CHECK: inline void takeKlassOpt(const Swift::Optional<Klass>& val) noexcept {
90+
// CHECK-NEXT: return _impl::$s11UseOptional12takeKlassOptyyAA0D0CSgF(_impl::swift_interop_passDirect_UseOptional_[[CLASSENC]](Swift::_impl::_impl_Optional<Klass>::getOpaquePointer(val)));
91+
// CHECK-NEXT: }
92+
93+
94+
// CHECK: inline void takeSmallStructOpt(const Swift::Optional<SmallStruct>& val) noexcept {
95+
// CHECK-NEXT: return _impl::$s11UseOptional18takeSmallStructOptyyAA0dE0VSgF(_impl::swift_interop_passDirect_UseOptional_uint32_t_0_4(Swift::_impl::_impl_Optional<SmallStruct>::getOpaquePointer(val)));
96+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)