Skip to content

Commit 15bef5e

Browse files
authored
Merge pull request #59529 from hyp/eng/mut-method-cpp
[interop][SwiftToCxx] support mutating struct methods in C++
2 parents bab11e8 + b47bb5b commit 15bef5e

File tree

5 files changed

+181
-13
lines changed

5 files changed

+181
-13
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,9 @@ class DeclAndTypePrinter::Implementation
908908
if (selfTypeDeclContext) {
909909
additionalParams.push_back(
910910
{DeclAndTypeClangFunctionPrinter::AdditionalParam::Role::Self,
911-
(*selfTypeDeclContext)->getDeclaredType()});
911+
(*selfTypeDeclContext)->getDeclaredType(),
912+
/*isIndirect=*/
913+
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false});
912914
}
913915
funcPrinter.printFunctionSignature(
914916
FD, funcABI.getSymbolName(), resultTy,

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
230230
ClangSyntaxPrinter(os).printBaseName(modifiers.qualifierContext);
231231
os << "::";
232232
}
233-
os << name << '(';
233+
ClangSyntaxPrinter(os).printIdentifier(name);
234+
os << '(';
234235

235236
bool HasParams = false;
236237
// Indirect result is passed in as the first parameter.
@@ -276,8 +277,13 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
276277
delegate.prefixIndirectParamValueTypeInC = [](raw_ostream &os) {
277278
os << "SWIFT_CONTEXT ";
278279
};
279-
print(param.type, OptionalTypeKind::OTK_None, "_self", /*isInOut*/ false,
280-
delegate);
280+
if (param.isIndirect) {
281+
(*delegate.prefixIndirectParamValueTypeInC)(os);
282+
os << "void * _Nonnull _self";
283+
} else {
284+
print(param.type, OptionalTypeKind::OTK_None, "_self",
285+
/*isInOut*/ false, delegate);
286+
}
281287
});
282288
}
283289
if (kind == FunctionSignatureKind::CFunctionProto && !HasParams) {
@@ -288,14 +294,14 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
288294
}
289295

290296
void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
291-
Type type, StringRef name, bool isInOut,
297+
Type type, StringRef name, bool isInOut, bool isIndirect,
292298
llvm::Optional<AdditionalParam::Role> paramRole) {
293299
auto namePrinter = [&]() { ClangSyntaxPrinter(os).printIdentifier(name); };
294300
if (!isKnownCxxType(type, typeMapping)) {
295301
if (auto *structDecl = type->getStructOrBoundGenericStruct()) {
296302
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
297303
.printParameterCxxToCUseScaffold(
298-
structDecl->isResilient() ||
304+
isIndirect || structDecl->isResilient() ||
299305
interopContext.getIrABIDetails().shouldPassIndirectly(type),
300306
structDecl, namePrinter, isInOut,
301307
/*isSelf=*/paramRole &&
@@ -331,6 +337,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
331337
if (params->size()) {
332338
if (hasParams)
333339
os << ", ";
340+
hasParams = true;
334341
size_t index = 1;
335342
interleaveComma(*params, os, [&](const ParamDecl *param) {
336343
if (param->hasName()) {
@@ -351,6 +358,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
351358
interleaveComma(additionalParams, os, [&](const AdditionalParam &param) {
352359
assert(param.role == AdditionalParam::Role::Self);
353360
printCxxToCFunctionParameterUse(param.type, "*this", /*isInOut=*/false,
361+
/*isIndirect=*/param.isIndirect,
354362
param.role);
355363
});
356364
}
@@ -396,18 +404,24 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
396404
modifiers.qualifierContext = typeDeclContext;
397405
printFunctionSignature(FD, FD->getName().getBaseIdentifier().get(), resultTy,
398406
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
399-
400-
// FIXME: Add support for mutating methods.
401-
os << " const";
407+
bool isMutating = false;
408+
if (auto *funcDecl = dyn_cast<FuncDecl>(FD))
409+
isMutating = funcDecl->isMutating();
410+
if (!isMutating)
411+
os << " const";
402412
if (!isDefinition) {
403413
os << ";\n";
404414
return;
405415
}
416+
406417
os << " {\n";
407418
// FIXME: should it be objTy for resultTy?
408419
printCxxThunkBody(swiftSymbolName, resultTy, FD->getParameters(),
409-
{AdditionalParam{AdditionalParam::Role::Self,
410-
typeDeclContext->getDeclaredType()}});
420+
{AdditionalParam{
421+
AdditionalParam::Role::Self,
422+
typeDeclContext->getDeclaredType(),
423+
/*isIndirect=*/isMutating,
424+
}});
411425
os << " }\n";
412426
}
413427

@@ -450,6 +464,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
450464
// FIXME: should it be objTy for resultTy?
451465
printCxxThunkBody(swiftSymbolName, resultTy, accessor->getParameters(),
452466
{AdditionalParam{AdditionalParam::Role::Self,
453-
typeDeclContext->getDeclaredType()}});
467+
typeDeclContext->getDeclaredType(),
468+
/*isIndirect=*/false}});
454469
os << " }\n";
455470
}

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class DeclAndTypeClangFunctionPrinter {
5656

5757
Role role;
5858
Type type;
59+
// Should self be passed indirectly?
60+
bool isIndirect = false;
5961
};
6062

6163
/// Optional modifiers that can be applied to function signature.
@@ -96,7 +98,7 @@ class DeclAndTypeClangFunctionPrinter {
9698

9799
private:
98100
void printCxxToCFunctionParameterUse(
99-
Type type, StringRef name, bool isInOut,
101+
Type type, StringRef name, bool isInOut, bool isIndirect = false,
100102
llvm::Optional<AdditionalParam::Role> paramRole = None);
101103

102104
raw_ostream &os;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/mutating-method-in-cxx.swift -typecheck -module-name Methods -clang-header-expose-public-decls -emit-clang-header-path %t/methods.h
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-methods-execution.o
6+
// RUN: %target-interop-build-swift %S/mutating-method-in-cxx.swift -o %t/swift-methods-execution -Xlinker %t/swift-methods-execution.o -module-name Methods -Xfrontend -entry-point-function-name -Xfrontend swiftMain
7+
8+
// RUN: %target-codesign %t/swift-methods-execution
9+
// RUN: %target-run %t/swift-methods-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
13+
#include <assert.h>
14+
#include "methods.h"
15+
16+
int main() {
17+
using namespace Methods;
18+
19+
auto smallStruct = createSmallStruct(10.0f);
20+
smallStruct.dump();
21+
// CHECK: small x = 10.0;
22+
23+
smallStruct.scale(0.25f).dump();
24+
smallStruct.dump();
25+
// CHECK-NEXT: small x = 10.0;
26+
// CHECK-NEXT: small x = 2.5;
27+
28+
smallStruct.invert();
29+
smallStruct.dump();
30+
// CHECK-NEXT: small x = -2.5;
31+
32+
auto largeStruct = createLargeStruct();
33+
largeStruct.dump();
34+
// CHECK-NEXT: 1, -5, 9, 11, 48879, -77
35+
36+
largeStruct.double_();
37+
largeStruct.dump();
38+
// CHECK-NEXT: 2, -10, 18, 22, 97758, -154
39+
40+
largeStruct.scale(-3, 10).dump();
41+
largeStruct.dump();
42+
// CHECK-NEXT: -6, -100, -54, 220, -293274, -1540
43+
// CHECK-NEXT: -6, -100, -54, 220, -293274, -1540
44+
return 0;
45+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Methods -clang-header-expose-public-decls -emit-clang-header-path %t/methods.h
3+
// RUN: %FileCheck %s < %t/methods.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/methods.h)
6+
7+
public struct LargeStruct {
8+
var x1, x2, x3, x4, x5, x6: Int
9+
10+
public func dump() {
11+
print("\(x1), \(x2), \(x3), \(x4), \(x5), \(x6)")
12+
}
13+
14+
public mutating func double() {
15+
x1 *= 2
16+
x2 *= 2
17+
x3 *= 2
18+
x4 *= 2
19+
x5 *= 2
20+
x6 *= 2
21+
}
22+
23+
public mutating func scale(_ x: Int, _ y: Int) -> LargeStruct {
24+
x1 *= x
25+
x2 *= y
26+
x3 *= x
27+
x4 *= y
28+
x5 *= x
29+
x6 *= y
30+
return self
31+
}
32+
}
33+
34+
public struct SmallStruct {
35+
var x: Float
36+
37+
public func dump() {
38+
print("small x = \(x);")
39+
}
40+
41+
public mutating func scale(_ y: Float) -> SmallStruct {
42+
x *= y
43+
return SmallStruct(x: x / y)
44+
}
45+
46+
public mutating func invert() {
47+
x = -x
48+
}
49+
}
50+
51+
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV4dumpyyF(SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
52+
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV6doubleyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // double()
53+
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV5scaleyACSi_SitF(SWIFT_INDIRECT_RESULT void * _Nonnull, ptrdiff_t x, ptrdiff_t y, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // scale(_:_:)
54+
55+
// CHECK: SWIFT_EXTERN void $s7Methods11SmallStructV4dumpyyF(struct swift_interop_stub_Methods_SmallStruct _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
56+
// CHECK: SWIFT_EXTERN struct swift_interop_stub_Methods_SmallStruct $s7Methods11SmallStructV5scaleyACSfF(float y, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // scale(_:)
57+
// CHECK: SWIFT_EXTERN void $s7Methods11SmallStructV6invertyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // invert()
58+
59+
// CHECK: class LargeStruct final {
60+
// CHECK: inline LargeStruct(LargeStruct &&) = default;
61+
// CHECK-NEXT: inline void dump() const;
62+
// CHECK-NEXT: inline void double_();
63+
// CHECK-NEXT: inline LargeStruct scale(swift::Int x, swift::Int y);
64+
// CHECK-NEXT: private
65+
66+
// CHECK: inline void LargeStruct::dump() const {
67+
// CHECK-NEXT: return _impl::$s7Methods11LargeStructV4dumpyyF(_getOpaquePointer());
68+
// CHECK-NEXT: }
69+
// CHECK-NEXT: inline void LargeStruct::double_() {
70+
// CHECK-NEXT: return _impl::$s7Methods11LargeStructV6doubleyyF(_getOpaquePointer());
71+
// CHECK-NEXT: }
72+
// CHECK-NEXT: inline LargeStruct LargeStruct::scale(swift::Int x, swift::Int y) {
73+
// CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](void * _Nonnull result) {
74+
// CHECK-NEXT: _impl::$s7Methods11LargeStructV5scaleyACSi_SitF(result, x, y, _getOpaquePointer());
75+
// CHECK-NEXT: });
76+
// CHECK-NEXT: }
77+
78+
// CHECK: class SmallStruct final {
79+
// CHECK: inline SmallStruct(SmallStruct &&) = default;
80+
// CHECK-NEXT: inline void dump() const;
81+
// CHECK-NEXT: inline SmallStruct scale(float y);
82+
// CHECK-NEXT: inline void invert();
83+
// CHECK-NEXT: private:
84+
85+
// CHECK: inline void SmallStruct::dump() const {
86+
// CHECK-NEXT: return _impl::$s7Methods11SmallStructV4dumpyyF(_impl::swift_interop_passDirect_Methods_SmallStruct(_getOpaquePointer()));
87+
// CHECK-NEXT: }
88+
// CHECK-NEXT: inline SmallStruct SmallStruct::scale(float y) {
89+
// CHECK-NEXT: return _impl::_impl_SmallStruct::returnNewValue([&](char * _Nonnull result) {
90+
// CHECK-NEXT: _impl::swift_interop_returnDirect_Methods_SmallStruct(result, _impl::$s7Methods11SmallStructV5scaleyACSfF(y, _getOpaquePointer()));
91+
// CHECK-NEXT: });
92+
// CHECK-NEXT: }
93+
// CHECK-NEXT: inline void SmallStruct::invert() {
94+
// CHECK-NEXT: return _impl::$s7Methods11SmallStructV6invertyyF(_getOpaquePointer());
95+
// CHECK-NEXT: }
96+
97+
98+
public func createLargeStruct() -> LargeStruct {
99+
return LargeStruct(x1: 1, x2: -5, x3: 9, x4: 11, x5: 0xbeef, x6: -77)
100+
}
101+
102+
public func createSmallStruct(x: Float) -> SmallStruct {
103+
return SmallStruct(x: x)
104+
}

0 commit comments

Comments
 (0)