@@ -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+ }
0 commit comments