Skip to content

Commit 776c5d6

Browse files
authored
Merge pull request #59418 from WANGJIEKE/cxx-interop-inout-struct
[interop][SwiftToCxx] Pass struct to function as inout
2 parents 795751c + 0505266 commit 776c5d6

9 files changed

+139
-41
lines changed

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 36 additions & 26 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,
@@ -77,62 +77,76 @@ class CFunctionSignatureTypePrinter
7777
languageMode(languageMode), typeUseKind(typeUseKind) {}
7878

7979
bool printIfKnownSimpleType(const TypeDecl *typeDecl,
80-
Optional<OptionalTypeKind> optionalKind) {
80+
Optional<OptionalTypeKind> optionalKind,
81+
bool isInOutParam) {
8182
auto knownTypeInfo = getKnownTypeInfo(typeDecl, typeMapping, languageMode);
8283
if (!knownTypeInfo)
8384
return false;
8485
os << knownTypeInfo->name;
85-
if (knownTypeInfo->canBeNullable)
86+
if (knownTypeInfo->canBeNullable) {
8687
printNullability(optionalKind);
88+
}
89+
if (isInOutParam) {
90+
os << (languageMode == swift::OutputLanguageMode::Cxx ? " &" : " *");
91+
}
8792
return true;
8893
}
8994

90-
void visitType(TypeBase *Ty, Optional<OptionalTypeKind> optionalKind) {
95+
void visitType(TypeBase *Ty, Optional<OptionalTypeKind> optionalKind,
96+
bool isInOutParam) {
9197
assert(Ty->getDesugaredType() == Ty && "unhandled sugared type");
9298
os << "/* ";
9399
Ty->print(os);
94100
os << " */";
95101
}
96102

97-
void visitTupleType(TupleType *TT, Optional<OptionalTypeKind> optionalKind) {
103+
void visitTupleType(TupleType *TT, Optional<OptionalTypeKind> optionalKind,
104+
bool isInOutParam) {
98105
assert(TT->getNumElements() == 0);
99106
// FIXME: Handle non-void type.
100107
os << "void";
101108
}
102109

103110
void visitTypeAliasType(TypeAliasType *aliasTy,
104-
Optional<OptionalTypeKind> optionalKind) {
111+
Optional<OptionalTypeKind> optionalKind,
112+
bool isInOutParam) {
105113
const TypeAliasDecl *alias = aliasTy->getDecl();
106-
if (printIfKnownSimpleType(alias, optionalKind))
114+
if (printIfKnownSimpleType(alias, optionalKind, isInOutParam))
107115
return;
108116

109-
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind);
117+
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
110118
}
111119

112-
void visitStructType(StructType *ST,
113-
Optional<OptionalTypeKind> optionalKind) {
120+
void visitStructType(StructType *ST, Optional<OptionalTypeKind> optionalKind,
121+
bool isInOutParam) {
114122
const StructDecl *SD = ST->getStructOrBoundGenericStruct();
115123

116124
// Handle known type names.
117-
if (printIfKnownSimpleType(SD, optionalKind))
125+
if (printIfKnownSimpleType(SD, optionalKind, isInOutParam))
118126
return;
119127
// FIXME: Handle optional structures.
120128
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
121129
if (languageMode != OutputLanguageMode::Cxx &&
122130
interopContext.getIrABIDetails().shouldPassIndirectly(ST)) {
123131
// FIXME: it would be nice to print out the C struct type here.
124-
os << "const void * _Nonnull";
132+
if (isInOutParam) {
133+
os << "void * _Nonnull";
134+
} else {
135+
os << "const void * _Nonnull";
136+
}
137+
125138
} else {
126139
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
127-
.printValueTypeParameterType(SD, languageMode);
140+
.printValueTypeParameterType(SD, languageMode, isInOutParam);
128141
}
129142
} else
130143
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
131144
.printValueTypeReturnType(SD, languageMode);
132145
}
133146

134-
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind) {
135-
TypeVisitor::visit(Ty, optionalKind);
147+
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind,
148+
bool isInOutParam) {
149+
TypeVisitor::visit(Ty, optionalKind, isInOutParam);
136150
}
137151

138152
private:
@@ -157,11 +171,7 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
157171
// see DeclAndTypePrinter::print.
158172
CFunctionSignatureTypePrinter typePrinter(os, cPrologueOS, typeMapping,
159173
outputLang, interopContext);
160-
typePrinter.visit(ty, optionalKind);
161-
162-
if (isInOutParam) {
163-
os << (outputLang == OutputLanguageMode::Cxx ? " &" : " *");
164-
}
174+
typePrinter.visit(ty, optionalKind, isInOutParam);
165175

166176
if (!name.empty()) {
167177
os << ' ';
@@ -182,7 +192,8 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
182192
CFunctionSignatureTypePrinter typePrinter(
183193
os, cPrologueOS, typeMapping, outputLang, interopContext,
184194
FunctionSignatureTypeUse::ReturnType);
185-
typePrinter.visit(objTy, retKind);
195+
// Param for indirect return cannot be marked as inout
196+
typePrinter.visit(objTy, retKind, /*isInOutParam=*/false);
186197
} else {
187198
os << "void";
188199
}
@@ -236,11 +247,14 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
236247
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
237248
.printParameterCxxToCUseScaffold(
238249
interopContext.getIrABIDetails().shouldPassIndirectly(type),
239-
structDecl, namePrinter);
250+
structDecl, namePrinter, param->isInOut());
240251
return;
241252
}
242253
}
243254
// Primitive types are passed directly without any conversions.
255+
if (param->isInOut()) {
256+
os << "&";
257+
}
244258
namePrinter();
245259
}
246260

@@ -261,10 +275,6 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
261275
os << ", ";
262276
size_t index = 1;
263277
interleaveComma(*params, os, [&](const ParamDecl *param) {
264-
if (param->isInOut()) {
265-
os << "&";
266-
}
267-
268278
if (param->hasName()) {
269279
printCxxToCFunctionParameterUse(param, param->getName().str());
270280
} else {

lib/PrintAsClang/PrintClangValueType.cpp

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

288288
void ClangValueTypePrinter::printValueTypeParameterType(
289-
const NominalTypeDecl *type, OutputLanguageMode outputLang) {
289+
const NominalTypeDecl *type, OutputLanguageMode outputLang,
290+
bool isInOutParam) {
290291
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
291292
if (outputLang != OutputLanguageMode::Cxx) {
292-
// C functions only take stub values directly as parameters.
293-
os << "struct ";
294-
printCStubTypeName(type);
293+
if (!isInOutParam) {
294+
// C functions only take stub values directly as parameters.
295+
os << "struct ";
296+
printCStubTypeName(type);
297+
} else {
298+
// Directly pass the pointer (from getOpaquePointer) to C interface
299+
// when in inout mode
300+
os << "char * _Nonnull";
301+
}
295302
return;
296303
}
297-
os << "const ";
304+
if (!isInOutParam) {
305+
os << "const ";
306+
}
298307
printCxxTypeName(os, type);
299308
os << '&';
300309
}
301310

302311
void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
303312
bool isIndirect, const NominalTypeDecl *type,
304-
llvm::function_ref<void()> cxxParamPrinter) {
313+
llvm::function_ref<void()> cxxParamPrinter, bool isInOut) {
305314
// A Swift value type is passed to its underlying Swift function
306315
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
307-
if (!isIndirect) {
316+
if (!isIndirect && !isInOut) {
308317
os << cxx_synthesis::getCxxImplNamespaceName() << "::"
309318
<< "swift_interop_passDirect_";
310319
printCTypeName(os, type);
@@ -315,8 +324,9 @@ void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
315324
os << "::getOpaquePointer(";
316325
cxxParamPrinter();
317326
os << ')';
318-
if (!isIndirect)
327+
if (!isIndirect && !isInOut) {
319328
os << ')';
329+
}
320330
}
321331

322332
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/functions/swift-primitive-inout-functions-cxx-bridging.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// CHECK: SWIFT_EXTERN void $s9Functions8inOutIntyySizF(ptrdiff_t * x) SWIFT_NOEXCEPT SWIFT_CALL; // inOutInt(_:)
88
// CHECK: SWIFT_EXTERN void $s9Functions11inOutTwoIntyySiz_SiztF(ptrdiff_t * x, ptrdiff_t * y) SWIFT_NOEXCEPT SWIFT_CALL; // inOutTwoInt(_:_:)
99
// CHECK: SWIFT_EXTERN void $s9Functions13inOutTwoParamyySbz_SdztF(bool * x, double * y) SWIFT_NOEXCEPT SWIFT_CALL; // inOutTwoParam(_:_:)
10+
// CHECK: SWIFT_EXTERN void $s9Functions24inoutTypeWithNullabilityyySVzF(void const * _Nonnull * x) SWIFT_NOEXCEPT SWIFT_CALL; // inoutTypeWithNullability(_:)
1011

1112
// CHECK: inline void inOutInt(swift::Int & x) noexcept {
1213
// CHECK-NEXT: return _impl::$s9Functions8inOutIntyySizF(&x);
@@ -20,6 +21,11 @@
2021
// CHECK-NEXT: return _impl::$s9Functions13inOutTwoParamyySbz_SdztF(&x, &y);
2122
// CHECK-NEXT: }
2223

24+
// CHECK: inline void inoutTypeWithNullability(void const * _Nonnull & x) noexcept {
25+
// CHECK-NEXT: return _impl::$s9Functions24inoutTypeWithNullabilityyySVzF(&x);
26+
// CHECK-NEXT: }
27+
28+
2329
public func inOutInt(_ x: inout Int) { x = Int() }
2430

2531
public func inOutTwoInt(_ x: inout Int, _ y: inout Int) {
@@ -31,3 +37,7 @@ public func inOutTwoParam(_ x: inout Bool, _ y: inout Double) {
3137
y = 3.14
3238
x = !x
3339
}
40+
41+
public func inoutTypeWithNullability(_ x: inout UnsafeRawPointer) {
42+
x += 1
43+
}

test/Interop/SwiftToCxx/functions/swift-primitive-inout-functions-execution.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ int main() {
3939
assert(x);
4040
assert(y == 3.14);
4141
}
42+
43+
{
44+
char c[2] = {'A', 'B'};
45+
const void *p = &c[0];
46+
assert(*static_cast<const char *>(p) == 'A');
47+
inoutTypeWithNullability(p);
48+
assert(*static_cast<const char *>(p) == 'B');
49+
}
4250
}

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)