Skip to content

Commit 7a3fe7c

Browse files
authored
Merge pull request #61121 from hyp/eng/expose-optional
[interop][SwiftToCxx] experimentally expose Swift's Optional type to C++
2 parents 7d21405 + 143ce1e commit 7a3fe7c

File tree

7 files changed

+295
-27
lines changed

7 files changed

+295
-27
lines changed

lib/IRGen/IRABIDetailsProvider.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ void LoweredFunctionSignature::visitParameterList(
302302
for (auto param : *FD->getParameters()) {
303303
// FIXME: tuples map to more than one sil param (but they're not yet
304304
// representable by the consumer).
305-
silParamMapping.push_back(param);
305+
if (!param->getInterfaceType()->isVoid())
306+
silParamMapping.push_back(param);
306307
}
307308
size_t currentSilParam = 0;
308309
for (const auto &abiParam : abiDetails.parameters) {

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: 49 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) {
@@ -211,6 +230,9 @@ class CFunctionSignatureTypePrinter
211230
if (TT->getNumElements() > 0)
212231
// FIXME: Handle non-void type.
213232
return ClangRepresentation::unsupported;
233+
// FIXME: how to support `()` parameters.
234+
if (typeUseKind != FunctionSignatureTypeUse::ReturnType)
235+
return ClangRepresentation::unsupported;
214236
os << "void";
215237
return ClangRepresentation::representable;
216238
}
@@ -238,14 +260,18 @@ class CFunctionSignatureTypePrinter
238260
bool isInOutParam) {
239261
// FIXME: handle optionalKind.
240262
if (languageMode != OutputLanguageMode::Cxx) {
241-
os << "void * _Nonnull";
263+
os << "void * "
264+
<< (!optionalKind || *optionalKind == OTK_None ? "_Nonnull"
265+
: "_Nullable");
242266
if (isInOutParam)
243267
os << " * _Nonnull";
244268
return ClangRepresentation::representable;
245269
}
246270
if (typeUseKind == FunctionSignatureTypeUse::ParamType && !isInOutParam)
247271
os << "const ";
248-
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
272+
printOptional(optionalKind, [&]() {
273+
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
274+
});
249275
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
250276
os << "&";
251277
return ClangRepresentation::representable;
@@ -295,9 +321,6 @@ class CFunctionSignatureTypePrinter
295321
if (!declPrinter.shouldInclude(decl))
296322
return ClangRepresentation::unsupported; // FIXME: propagate why it's not
297323
// exposed.
298-
// FIXME: Support Optional<T>.
299-
if (optionalKind && *optionalKind != OTK_None)
300-
return ClangRepresentation::unsupported;
301324
// Only C++ mode supports struct types.
302325
if (languageMode != OutputLanguageMode::Cxx)
303326
return ClangRepresentation::unsupported;
@@ -309,22 +332,27 @@ class CFunctionSignatureTypePrinter
309332
if (typeUseKind == FunctionSignatureTypeUse::ParamType &&
310333
!isInOutParam)
311334
os << "const ";
312-
handler.printTypeName(os);
335+
printOptional(optionalKind, [&]() { handler.printTypeName(os); });
313336
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
314337
os << '&';
315338
return ClangRepresentation::representable;
316339
}
317340

318-
// FIXME: Handle optional structures.
319341
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
320342
if (!isInOutParam) {
321343
os << "const ";
322344
}
323-
ClangSyntaxPrinter(os).printPrimaryCxxTypeName(decl, moduleContext);
324-
auto result = visitGenericArgs(genericArgs);
345+
ClangRepresentation result = ClangRepresentation::representable;
346+
printOptional(optionalKind, [&]() {
347+
ClangSyntaxPrinter(os).printPrimaryCxxTypeName(decl, moduleContext);
348+
result = visitGenericArgs(genericArgs);
349+
});
325350
os << '&';
326351
return result;
327-
} else {
352+
}
353+
354+
ClangRepresentation result = ClangRepresentation::representable;
355+
printOptional(optionalKind, [&]() {
328356
ClangValueTypePrinter printer(os, cPrologueOS, interopContext);
329357
printer.printValueTypeReturnType(
330358
decl, languageMode,
@@ -333,9 +361,9 @@ class CFunctionSignatureTypePrinter
333361
ClangValueTypePrinter::TypeUseKind::CxxTypeName)
334362
: ClangValueTypePrinter::TypeUseKind::CxxTypeName,
335363
moduleContext);
336-
return visitGenericArgs(genericArgs);
337-
}
338-
return ClangRepresentation::representable;
364+
result = visitGenericArgs(genericArgs);
365+
});
366+
return result;
339367
}
340368

341369
Optional<ClangRepresentation>
@@ -393,13 +421,10 @@ class CFunctionSignatureTypePrinter
393421
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
394422
Optional<OptionalTypeKind> optionalKind,
395423
bool isInOutParam) {
396-
// FIXME: Support Optional<T>.
397-
if (optionalKind && *optionalKind != OTK_None)
398-
return ClangRepresentation::unsupported;
399424
bool isParam = typeUseKind == FunctionSignatureTypeUse::ParamType;
400425
if (isParam && !isInOutParam)
401426
os << "const ";
402-
// FIXME: handle optionalKind.
427+
403428
if (languageMode != OutputLanguageMode::Cxx) {
404429
// Note: This can happen for UnsafeMutablePointer<T>.
405430
if (typeUseKind != FunctionSignatureTypeUse::ParamType)
@@ -409,7 +434,9 @@ class CFunctionSignatureTypePrinter
409434
os << "void * _Nonnull";
410435
return ClangRepresentation::representable;
411436
}
412-
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(genericTpt);
437+
printOptional(optionalKind, [&]() {
438+
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(genericTpt);
439+
});
413440
// Pass a reference to the template type.
414441
if (isParam)
415442
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)