Skip to content

Commit 8f00df5

Browse files
committed
cdecl thunks: property initialize indirect returns via initialize(to:)
This requires us to walk the parallel structure between indirect returns (where we need to introduce the initialize(to:) calls instead of pointee references) and the Swift value.
1 parent 97299cc commit 8f00df5

File tree

2 files changed

+79
-23
lines changed

2 files changed

+79
-23
lines changed

Sources/JExtractSwift/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -430,17 +430,6 @@ extension LoweredFunctionSignature {
430430
originalResult = callExpression
431431
}
432432

433-
// FIXME: Check whether there are multiple places in which we reference
434-
// the placeholder in resultConversion. If so, we should write it into a
435-
// local let "_resultValue" or similar so we don't call the underlying
436-
// function multiple times.
437-
438-
// Convert the result.
439-
let convertedResult = resultConversion.asExprSyntax(
440-
isSelf: true,
441-
placeholder: originalResult.description
442-
)
443-
444433
if cdecl.result.type.isVoid {
445434
// Indirect return. This is a regular return in Swift that turns
446435
// into an assignment via the indirect parameters. We do a cdeclToSwift
@@ -450,16 +439,23 @@ extension LoweredFunctionSignature {
450439
let cdeclParamConversion = try! ConversionStep(
451440
cdeclToSwift: original.result.type
452441
)
453-
let indirectResults = cdeclParamConversion.asExprSyntax(
454-
isSelf: true,
455-
placeholder: "_result"
456-
)
457-
bodyItems.append("""
458-
\(indirectResults) = \(convertedResult)
459-
"""
442+
443+
// For each indirect result, initialize the value directly with the
444+
// corresponding element in the converted result.
445+
bodyItems.append(
446+
contentsOf: cdeclParamConversion.initialize(
447+
placeholder: "_result",
448+
from: resultConversion,
449+
otherPlaceholder: originalResult.description
450+
)
460451
)
461452
} else {
462453
// Direct return. Just convert the expression.
454+
let convertedResult = resultConversion.asExprSyntax(
455+
isSelf: true,
456+
placeholder: originalResult.description
457+
)
458+
463459
bodyItems.append("""
464460
return \(convertedResult)
465461
"""
@@ -475,3 +471,58 @@ extension LoweredFunctionSignature {
475471
return loweredCDecl
476472
}
477473
}
474+
475+
extension ConversionStep {
476+
/// Form a set of statements that initializes the placeholders within
477+
/// the given conversion step from ones in the other step, effectively
478+
/// exploding something like `(a, (b, c)) = (d, (e, f))` into
479+
/// separate initializations for a, b, and c from d, e, and f, respectively.
480+
func initialize(
481+
placeholder: String,
482+
from otherStep: ConversionStep,
483+
otherPlaceholder: String
484+
) -> [CodeBlockItemSyntax] {
485+
// Create separate assignments for each element in paired tuples.
486+
if case .tuplify(let elements) = self,
487+
case .tuplify(let otherElements) = otherStep {
488+
assert(elements.count == otherElements.count)
489+
490+
return elements.indices.flatMap { index in
491+
elements[index].initialize(
492+
placeholder: "\(placeholder)_\(index)",
493+
from: otherElements[index],
494+
otherPlaceholder: "\(otherPlaceholder)_\(index)"
495+
)
496+
}
497+
}
498+
499+
// Look through "pass indirectly" steps; they do nothing here.
500+
if case .passIndirectly(let conversionStep) = self {
501+
return conversionStep.initialize(
502+
placeholder: placeholder,
503+
from: otherStep,
504+
otherPlaceholder: otherPlaceholder
505+
)
506+
}
507+
508+
// The value we're initializing from.
509+
let otherExpr = otherStep.asExprSyntax(
510+
isSelf: false,
511+
placeholder: otherPlaceholder
512+
)
513+
514+
// If we have a "pointee" on where we are performing initialization, we
515+
// need to instead produce an initialize(to:) call.
516+
if case .pointee(let innerSelf) = self {
517+
let selfPointerExpr = innerSelf.asExprSyntax(
518+
isSelf: true,
519+
placeholder: placeholder
520+
)
521+
522+
return [ " \(selfPointerExpr).initialize(to: \(otherExpr))" ]
523+
}
524+
525+
let selfExpr = self.asExprSyntax(isSelf: true, placeholder: placeholder)
526+
return [ " \(selfExpr) = \(otherExpr)" ]
527+
}
528+
}

Tests/JExtractSwiftTests/FunctionLoweringTests.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ final class FunctionLoweringTests {
7979
expectedCDecl: """
8080
@_cdecl("c_shifted")
8181
func c_shifted(_ delta_0: Double, _ delta_1: Double, _ self: UnsafeRawPointer, _ _result: UnsafeMutableRawPointer) {
82-
_result.assumingMemoryBound(to: Point.self).pointee = self.assumingMemoryBound(to: Point.self).pointee.shifted(by: (delta_0, delta_1))
82+
_result.assumingMemoryBound(to: Point.self).initialize(to: self.assumingMemoryBound(to: Point.self).pointee.shifted(by: (delta_0, delta_1)))
8383
}
8484
""",
8585
expectedCFunction: "void c_shifted(double delta_0, double delta_1, void const* self, void* _result)"
@@ -136,7 +136,7 @@ final class FunctionLoweringTests {
136136
expectedCDecl: """
137137
@_cdecl("c_scaledUnit")
138138
func c_scaledUnit(_ value: Double, _ _result: UnsafeMutableRawPointer) {
139-
_result.assumingMemoryBound(to: Point.self).pointee = Point.scaledUnit(by: value)
139+
_result.assumingMemoryBound(to: Point.self).initialize(to: Point.scaledUnit(by: value))
140140
}
141141
""",
142142
expectedCFunction: "void c_scaledUnit(double value, void* _result)"
@@ -171,7 +171,7 @@ final class FunctionLoweringTests {
171171
expectedCDecl: """
172172
@_cdecl("c_init")
173173
func c_init(_ value: Double, _ _result: UnsafeMutableRawPointer) {
174-
_result.assumingMemoryBound(to: Point.self).pointee = Point(scaledBy: value)
174+
_result.assumingMemoryBound(to: Point.self).initialize(to: Point(scaledBy: value))
175175
}
176176
""",
177177
expectedCFunction: "void c_init(double value, void* _result)"
@@ -261,13 +261,18 @@ final class FunctionLoweringTests {
261261
@Test("Lowering tuple returns")
262262
func lowerTupleReturns() throws {
263263
try assertLoweredFunction("""
264-
func getTuple() -> (Int, (Float, Double)) { }
264+
func getTuple() -> (Int, (Float, Point)) { }
265+
""",
266+
sourceFile: """
267+
struct Point { }
265268
""",
266269
expectedCDecl: """
267270
@_cdecl("c_getTuple")
268271
func c_getTuple(_ _result_0: UnsafeMutableRawPointer, _ _result_1_0: UnsafeMutableRawPointer, _ _result_1_1: UnsafeMutableRawPointer) {
269272
let __swift_result = getTuple()
270-
(_result_0, (_result_1_0, _result_1_1)) = (__swift_result_0, (__swift_result_1_0, __swift_result_1_1))
273+
_result_0 = __swift_result_0
274+
_result_1_0 = __swift_result_1_0
275+
_result_1_1.assumingMemoryBound(to: Point.self).initialize(to: __swift_result_1_1)
271276
}
272277
""",
273278
expectedCFunction: "void c_getTuple(void* _result_0, void* _result_1_0, void* _result_1_1)"

0 commit comments

Comments
 (0)