Skip to content

Commit f033aad

Browse files
committed
[interop][SwiftToCxx] pass struct to function as inout
1 parent 2d2a56f commit f033aad

7 files changed

+120
-34
lines changed

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ bool isKnownCType(Type t, PrimitiveTypeMapping &typeMapping) {
6363
// native Swift function/method.
6464
class CFunctionSignatureTypePrinter
6565
: public TypeVisitor<CFunctionSignatureTypePrinter, void,
66-
Optional<OptionalTypeKind>>,
66+
Optional<OptionalTypeKind>, bool>,
6767
private ClangSyntaxPrinter {
6868
public:
6969
CFunctionSignatureTypePrinter(raw_ostream &os, raw_ostream &cPrologueOS,
@@ -87,30 +87,33 @@ class CFunctionSignatureTypePrinter
8787
return true;
8888
}
8989

90-
void visitType(TypeBase *Ty, Optional<OptionalTypeKind> optionalKind) {
90+
void visitType(TypeBase *Ty, Optional<OptionalTypeKind> optionalKind,
91+
bool isInOutParam) {
9192
assert(Ty->getDesugaredType() == Ty && "unhandled sugared type");
9293
os << "/* ";
9394
Ty->print(os);
9495
os << " */";
9596
}
9697

97-
void visitTupleType(TupleType *TT, Optional<OptionalTypeKind> optionalKind) {
98+
void visitTupleType(TupleType *TT, Optional<OptionalTypeKind> optionalKind,
99+
bool isInOutParam) {
98100
assert(TT->getNumElements() == 0);
99101
// FIXME: Handle non-void type.
100102
os << "void";
101103
}
102104

103105
void visitTypeAliasType(TypeAliasType *aliasTy,
104-
Optional<OptionalTypeKind> optionalKind) {
106+
Optional<OptionalTypeKind> optionalKind,
107+
bool isInOutParam) {
105108
const TypeAliasDecl *alias = aliasTy->getDecl();
106109
if (printIfKnownSimpleType(alias, optionalKind))
107110
return;
108111

109-
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind);
112+
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
110113
}
111114

112-
void visitStructType(StructType *ST,
113-
Optional<OptionalTypeKind> optionalKind) {
115+
void visitStructType(StructType *ST, Optional<OptionalTypeKind> optionalKind,
116+
bool isInOutParam) {
114117
const StructDecl *SD = ST->getStructOrBoundGenericStruct();
115118

116119
// Handle known type names.
@@ -121,18 +124,24 @@ class CFunctionSignatureTypePrinter
121124
if (languageMode != OutputLanguageMode::Cxx &&
122125
interopContext.getIrABIDetails().shouldPassIndirectly(ST)) {
123126
// FIXME: it would be nice to print out the C struct type here.
124-
os << "const void * _Nonnull";
127+
if (isInOutParam) {
128+
os << "void * _Nonnull";
129+
} else {
130+
os << "const void * _Nonnull";
131+
}
132+
125133
} else {
126134
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
127-
.printValueTypeParameterType(SD, languageMode);
135+
.printValueTypeParameterType(SD, languageMode, isInOutParam);
128136
}
129137
} else
130138
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
131139
.printValueTypeReturnType(SD, languageMode);
132140
}
133141

134-
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind) {
135-
TypeVisitor::visit(Ty, optionalKind);
142+
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind,
143+
bool isInOutParam) {
144+
TypeVisitor::visit(Ty, optionalKind, isInOutParam);
136145
}
137146

138147
private:
@@ -157,10 +166,16 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
157166
// see DeclAndTypePrinter::print.
158167
CFunctionSignatureTypePrinter typePrinter(os, cPrologueOS, typeMapping,
159168
outputLang, interopContext);
160-
typePrinter.visit(ty, optionalKind);
169+
typePrinter.visit(ty, optionalKind, isInOutParam);
161170

162171
if (isInOutParam) {
163-
os << (outputLang == OutputLanguageMode::Cxx ? " &" : " *");
172+
if (outputLang == OutputLanguageMode::Cxx &&
173+
isKnownCxxType(ty, typeMapping)) {
174+
os << " &";
175+
} else if (outputLang == OutputLanguageMode::ObjC &&
176+
isKnownCType(ty, typeMapping)) {
177+
os << " *";
178+
}
164179
}
165180

166181
if (!name.empty()) {
@@ -182,7 +197,9 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
182197
CFunctionSignatureTypePrinter typePrinter(
183198
os, cPrologueOS, typeMapping, outputLang, interopContext,
184199
FunctionSignatureTypeUse::ReturnType);
185-
typePrinter.visit(objTy, retKind);
200+
// Pass false because it's a parameter for indirect return.
201+
// Not an actual parameter, which means it can never be inout
202+
typePrinter.visit(objTy, retKind, false);
186203
} else {
187204
os << "void";
188205
}
@@ -236,11 +253,14 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
236253
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
237254
.printParameterCxxToCUseScaffold(
238255
interopContext.getIrABIDetails().shouldPassIndirectly(type),
239-
structDecl, namePrinter);
256+
structDecl, namePrinter, param->isInOut());
240257
return;
241258
}
242259
}
243260
// Primitive types are passed directly without any conversions.
261+
if (param->isInOut()) {
262+
os << "&";
263+
}
244264
namePrinter();
245265
}
246266

@@ -261,10 +281,6 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
261281
os << ", ";
262282
size_t index = 1;
263283
interleaveComma(*params, os, [&](const ParamDecl *param) {
264-
if (param->isInOut()) {
265-
os << "&";
266-
}
267-
268284
if (param->hasName()) {
269285
printCxxToCFunctionParameterUse(param, param->getName().str());
270286
} else {

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,25 +224,34 @@ void ClangValueTypePrinter::printCStubTypeName(const NominalTypeDecl *type) {
224224
}
225225

226226
void ClangValueTypePrinter::printValueTypeParameterType(
227-
const NominalTypeDecl *type, OutputLanguageMode outputLang) {
227+
const NominalTypeDecl *type, OutputLanguageMode outputLang,
228+
bool isInOutParam) {
228229
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
229230
if (outputLang != OutputLanguageMode::Cxx) {
230-
// C functions only take stub values directly as parameters.
231-
os << "struct ";
232-
printCStubTypeName(type);
231+
if (!isInOutParam) {
232+
// C functions only take stub values directly as parameters.
233+
os << "struct ";
234+
printCStubTypeName(type);
235+
} else {
236+
// Directly pass the pointer (from getOpaquePointer) to C interface
237+
// when in inout mode
238+
os << "char * _Nonnull";
239+
}
233240
return;
234241
}
235-
os << "const ";
242+
if (!isInOutParam) {
243+
os << "const ";
244+
}
236245
printCxxTypeName(os, type);
237246
os << '&';
238247
}
239248

240249
void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
241250
bool isIndirect, const NominalTypeDecl *type,
242-
llvm::function_ref<void()> cxxParamPrinter) {
251+
llvm::function_ref<void()> cxxParamPrinter, bool isInOut) {
243252
// A Swift value type is passed to its underlying Swift function
244253
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
245-
if (!isIndirect) {
254+
if (!isIndirect && !isInOut) {
246255
os << cxx_synthesis::getCxxImplNamespaceName() << "::"
247256
<< "swift_interop_passDirect_";
248257
printCTypeName(os, type);
@@ -253,8 +262,9 @@ void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
253262
os << "::getOpaquePointer(";
254263
cxxParamPrinter();
255264
os << ')';
256-
if (!isIndirect)
265+
if (!isIndirect && !isInOut) {
257266
os << ')';
267+
}
258268
}
259269

260270
void ClangValueTypePrinter::printValueTypeReturnType(

lib/PrintAsClang/PrintClangValueType.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ class ClangValueTypePrinter {
4141

4242
/// Print the pararameter type that referes to a Swift struct type in C/C++.
4343
void printValueTypeParameterType(const NominalTypeDecl *type,
44-
OutputLanguageMode outputLang);
44+
OutputLanguageMode outputLang,
45+
bool isInOutParam);
4546

4647
/// Print the use of a C++ struct/enum parameter value as it's passed to the
4748
/// underlying C function that represents the native Swift function.
4849
void
4950
printParameterCxxToCUseScaffold(bool isIndirect, const NominalTypeDecl *type,
50-
llvm::function_ref<void()> cxxParamPrinter);
51+
llvm::function_ref<void()> cxxParamPrinter,
52+
bool isInOut);
5153

5254
/// Print the return type that refers to a Swift struct type in C/C++.
5355
void printValueTypeReturnType(const NominalTypeDecl *typeDecl,

test/Interop/SwiftToCxx/structs/large-structs-pass-return-indirect-in-cxx-execution.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,13 @@ int main() {
2424
StructSeveralI64 structSeveralI64_copy = passThroughStructSeveralI64(100, returnNewStructSeveralI64(11), 6.0);
2525
printStructSeveralI64(structSeveralI64_copy);
2626
// CHECK-NEXT: StructSeveralI64.1 = 11, .2 = 100, .3 = -17, .4 = -12345612, .5 = -65529
27+
28+
auto myStruct = returnNewStructSeveralI64(99);
29+
printStructSeveralI64(myStruct);
30+
// CHECK-NEXT: StructSeveralI64.1 = 99, .2 = 0, .3 = -17, .4 = 12345612, .5 = -65535
31+
32+
inoutStructSeveralI64(myStruct);
33+
printStructSeveralI64(myStruct);
34+
// CHECK-NEXT: StructSeveralI64.1 = -1, .2 = -2, .3 = -3, .4 = -4, .5 = -5
2735
return 0;
2836
}

test/Interop/SwiftToCxx/structs/large-structs-pass-return-indirect-in-cxx.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
// RUN: %check-interop-cxx-header-in-clang(%t/structs.h)
66

77
public struct StructSeveralI64 {
8-
let x1, x2, x3, x4, x5: Int64
8+
var x1, x2, x3, x4, x5: Int64
99
}
1010

11+
// CHECK: SWIFT_EXTERN void $s7Structs21inoutStructSeveralI64yyAA0cdE0VzF(void * _Nonnull s) SWIFT_NOEXCEPT SWIFT_CALL; // inoutStructSeveralI64(_:)
1112
// CHECK: class StructSeveralI64 final {
1213

1314
public func returnNewStructSeveralI64(i: Int64) -> StructSeveralI64 {
@@ -22,6 +23,19 @@ public func printStructSeveralI64(_ x: StructSeveralI64) {
2223
print("StructSeveralI64.1 = \(x.x1), .2 = \(x.x2), .3 = \(x.x3), .4 = \(x.x4), .5 = \(x.x5)")
2324
}
2425

26+
public func inoutStructSeveralI64(_ s: inout StructSeveralI64) {
27+
s.x1 = -1
28+
s.x2 = -2
29+
s.x3 = -3
30+
s.x4 = -4
31+
s.x5 = -5
32+
}
33+
34+
// CHECK: inline void inoutStructSeveralI64(StructSeveralI64& s) noexcept {
35+
// CHECK-NEXT: return _impl::$s7Structs21inoutStructSeveralI64yyAA0cdE0VzF(_impl::_impl_StructSeveralI64::getOpaquePointer(s));
36+
// CHECK-NEXT: }
37+
38+
2539
// CHECK: inline StructSeveralI64 passThroughStructSeveralI64(int64_t i, const StructSeveralI64& x, float j) noexcept SWIFT_WARN_UNUSED_RESULT {
2640
// CHECK-NEXT: return _impl::_impl_StructSeveralI64::returnNewValue([&](void * _Nonnull result) {
2741
// CHECK-NEXT: _impl::$s7Structs27passThroughStructSeveralI641i_1jAA0deF0Vs5Int64V_AFSftF(result, i, _impl::_impl_StructSeveralI64::getOpaquePointer(x), j);

test/Interop/SwiftToCxx/structs/small-structs-pass-return-direct-in-cxx-execution.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,18 @@ int main() {
4747
auto structDoubleAndFloat = returnNewStructDoubleAndFloat(floatValue, doubleValue);
4848
assert(getStructDoubleAndFloat_x(structDoubleAndFloat) == doubleValue);
4949
assert(getStructDoubleAndFloat_y(structDoubleAndFloat) == floatValue);
50+
51+
// s = StructOneI16AndOneStruct(x: 0xFF, y: StructTwoI32(x: 5, y: 72))
52+
auto s = returnNewStructOneI16AndOneStruct();
53+
// s2 = StructTwoI32(x: 10, y: 20)
54+
auto s2 = returnNewStructTwoI32(10);
55+
inoutStructOneI16AndOneStruct(s, s2);
56+
printStructStructTwoI32_and_OneI16AndOneStruct(s2, s);
57+
// CHECK-NEXT: StructTwoI32.x = 10, y = 20
58+
// CHECK-NEXT: StructOneI16AndOneStruct.x = 205, y.x = 10, y.y = 20
59+
60+
inoutStructDoubleAndFloat(structDoubleAndFloat);
61+
assert(getStructDoubleAndFloat_x(structDoubleAndFloat) == doubleValue * floatValue);
62+
assert(getStructDoubleAndFloat_y(structDoubleAndFloat) == floatValue / 10);
5063
return 0;
5164
}

test/Interop/SwiftToCxx/structs/small-structs-pass-return-direct-in-cxx.swift

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ public struct StructTwoI32 {
1313
}
1414

1515
public struct StructOneI16AndOneStruct {
16-
let x: Int16
17-
let y: StructTwoI32
16+
var x: Int16
17+
var y: StructTwoI32
1818
}
1919

2020
public struct StructU16AndPointer {
@@ -23,10 +23,13 @@ public struct StructU16AndPointer {
2323
}
2424

2525
public struct StructDoubleAndFloat {
26-
let x: Double
27-
let y: Float
26+
var x: Double
27+
var y: Float
2828
}
2929

30+
// CHECK: SWIFT_EXTERN void $s7Structs25inoutStructDoubleAndFloatyyAA0cdeF0VzF(char * _Nonnull s) SWIFT_NOEXCEPT SWIFT_CALL; // inoutStructDoubleAndFloat(_:)
31+
// CHECK: SWIFT_EXTERN void $s7Structs020inoutStructOneI16AnddC0yyAA0cdefdC0Vz_AA0C6TwoI32VtF(char * _Nonnull s, struct swift_interop_stub_Structs_StructTwoI32 s2) SWIFT_NOEXCEPT SWIFT_CALL; // inoutStructOneI16AndOneStruct(_:_:)
32+
3033
// CHECK: class StructDoubleAndFloat final {
3134

3235
// CHECK: class StructOneI16AndOneStruct final {
@@ -64,6 +67,11 @@ public func printStructStructTwoI32_and_OneI16AndOneStruct(_ y: StructTwoI32, _
6467
print("StructOneI16AndOneStruct.x = \(x.x), y.x = \(x.y.x), y.y = \(x.y.y)")
6568
}
6669

70+
public func inoutStructOneI16AndOneStruct(_ s: inout StructOneI16AndOneStruct, _ s2: StructTwoI32) {
71+
s.x -= 50
72+
s.y = s2
73+
}
74+
6775
public func returnNewStructU16AndPointer(_ x: UnsafeMutableRawPointer) -> StructU16AndPointer {
6876
return StructU16AndPointer(x: 55, y: x)
6977
}
@@ -80,6 +88,11 @@ public func getStructDoubleAndFloat_x(_ x: StructDoubleAndFloat) -> Double { ret
8088

8189
public func getStructDoubleAndFloat_y(_ x: StructDoubleAndFloat) -> Float { return x.y }
8290

91+
public func inoutStructDoubleAndFloat(_ s: inout StructDoubleAndFloat) {
92+
s.x *= Double(s.y)
93+
s.y /= 10
94+
}
95+
8396
// CHECK: inline double getStructDoubleAndFloat_x(const StructDoubleAndFloat& x) noexcept SWIFT_WARN_UNUSED_RESULT {
8497
// CHECK-NEXT: return _impl::$s7Structs25getStructDoubleAndFloat_xySdAA0cdeF0VF(_impl::swift_interop_passDirect_Structs_StructDoubleAndFloat(_impl::_impl_StructDoubleAndFloat::getOpaquePointer(x)));
8598
// CHECK-NEXT: }
@@ -100,6 +113,16 @@ public func getStructDoubleAndFloat_y(_ x: StructDoubleAndFloat) -> Float { retu
100113
// CHECK-NEXT: }
101114

102115

116+
// CHECK: inline void inoutStructDoubleAndFloat(StructDoubleAndFloat& s) noexcept {
117+
// CHECK-NEXT: return _impl::$s7Structs25inoutStructDoubleAndFloatyyAA0cdeF0VzF(_impl::_impl_StructDoubleAndFloat::getOpaquePointer(s));
118+
// CHECK-NEXT: }
119+
120+
121+
// CHECK: inline void inoutStructOneI16AndOneStruct(StructOneI16AndOneStruct& s, const StructTwoI32& s2) noexcept {
122+
// CHECK-NEXT: return _impl::$s7Structs020inoutStructOneI16AnddC0yyAA0cdefdC0Vz_AA0C6TwoI32VtF(_impl::_impl_StructOneI16AndOneStruct::getOpaquePointer(s), _impl::swift_interop_passDirect_Structs_StructTwoI32(_impl::_impl_StructTwoI32::getOpaquePointer(s2)));
123+
// CHECK-NEXT: }
124+
125+
103126
// CHECK: inline StructOneI64 passThroughStructOneI64(const StructOneI64& x) noexcept SWIFT_WARN_UNUSED_RESULT {
104127
// CHECK-NEXT: return _impl::_impl_StructOneI64::returnNewValue([&](char * _Nonnull result) {
105128
// CHECK-NEXT: _impl::swift_interop_returnDirect_Structs_StructOneI64(result, _impl::$s7Structs23passThroughStructOneI64yAA0deF0VADF(_impl::swift_interop_passDirect_Structs_StructOneI64(_impl::_impl_StructOneI64::getOpaquePointer(x))));

0 commit comments

Comments
 (0)