Skip to content

Commit c491f1b

Browse files
committed
Implement reabstraction of inouts in witness and reabstraction thunks.
This is a longstanding gap in the implementation that quite possibly never bit anyone in the wild. I've only gotten around to implementing it now because this same code path will be used to reabstract inout yields. That means that, rather than only affecting functions with an abstraction difference in an inout parameter type, this can affect all storage with any abstraction difference in the stored value type. So it's time to fill in this gap.
1 parent fae2ec3 commit c491f1b

File tree

2 files changed

+124
-7
lines changed

2 files changed

+124
-7
lines changed

lib/SILGen/SILGenPoly.cpp

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,53 @@ static CanType getSingleTupleElement(CanType type) {
827827
}
828828

829829
namespace {
830+
class TranslateIndirect : public Cleanup {
831+
AbstractionPattern InputOrigType, OutputOrigType;
832+
CanType InputSubstType, OutputSubstType;
833+
SILValue Input, Output;
834+
835+
public:
836+
TranslateIndirect(AbstractionPattern inputOrigType, CanType inputSubstType,
837+
AbstractionPattern outputOrigType, CanType outputSubstType,
838+
SILValue input, SILValue output)
839+
: InputOrigType(inputOrigType), OutputOrigType(outputOrigType),
840+
InputSubstType(inputSubstType), OutputSubstType(outputSubstType),
841+
Input(input), Output(output) {
842+
assert(input->getType().isAddress());
843+
assert(output->getType().isAddress());
844+
}
845+
846+
void emit(SILGenFunction &SGF, CleanupLocation loc,
847+
ForUnwind_t forUnwind) override {
848+
FullExpr scope(SGF.Cleanups, loc);
849+
850+
// Re-assert ownership of the input value.
851+
auto inputMV = SGF.emitManagedBufferWithCleanup(Input);
852+
853+
// Set up an initialization of the output buffer.
854+
auto &outputTL = SGF.getTypeLowering(Output->getType());
855+
auto outputInit = SGF.useBufferAsTemporary(Output, outputTL);
856+
857+
// Transform into the output buffer.
858+
auto mv = SGF.emitTransformedValue(loc, inputMV,
859+
InputOrigType, InputSubstType,
860+
OutputOrigType, OutputSubstType,
861+
SGFContext(outputInit.get()));
862+
emitForceInto(SGF, loc, mv, *outputInit);
863+
864+
// Disable the cleanup; we've kept our promise to leave the inout
865+
// initialized.
866+
outputInit->getManagedAddress().forward(SGF);
867+
}
868+
869+
void dump(SILGenFunction &SGF) const override {
870+
llvm::errs() << "TranslateIndirect("
871+
<< InputOrigType << ", " << InputSubstType << ", "
872+
<< OutputOrigType << ", " << OutputSubstType << ", "
873+
<< Output << ", " << Input << ")\n";
874+
}
875+
};
876+
830877
class TranslateArguments {
831878
SILGenFunction &SGF;
832879
SILLocation Loc;
@@ -1356,13 +1403,49 @@ namespace {
13561403
outputSubstType, input);
13571404
return;
13581405
case ParameterConvention::Indirect_Inout: {
1359-
// If it's inout, we need writeback.
1360-
llvm::errs() << "inout writeback in abstraction difference thunk "
1361-
"not yet implemented\n";
1362-
llvm::errs() << "input value ";
1363-
input.getValue()->dump();
1364-
llvm::errs() << "output type " << SGF.getSILType(result) << "\n";
1365-
abort();
1406+
inputOrigType = inputOrigType.getWithoutSpecifierType();
1407+
inputSubstType = inputSubstType.getWithoutSpecifierType();
1408+
outputOrigType = outputOrigType.getWithoutSpecifierType();
1409+
outputSubstType = outputSubstType.getWithoutSpecifierType();
1410+
1411+
// Create a temporary of the right type.
1412+
auto &temporaryTL = SGF.getTypeLowering(result.getType());
1413+
auto temporary = SGF.emitTemporary(Loc, temporaryTL);
1414+
1415+
// Take ownership of the input value. This leaves the input l-value
1416+
// effectively uninitialized, but we'll push a cleanup that will put
1417+
// a value back into it.
1418+
FullExpr scope(SGF.Cleanups, CleanupLocation::get(Loc));
1419+
auto ownedInput =
1420+
SGF.emitManagedBufferWithCleanup(input.getLValueAddress());
1421+
1422+
// Translate the input value into the temporary.
1423+
translateSingleInto(inputOrigType, inputSubstType,
1424+
outputOrigType, outputSubstType,
1425+
ownedInput, *temporary);
1426+
1427+
// Forward the cleanup on the temporary. We're about to push a new
1428+
// cleanup that will re-assert ownership of this value.
1429+
auto temporaryAddr = temporary->getManagedAddress().forward(SGF);
1430+
1431+
// Leave the scope in which we did the forward translation. This
1432+
// ensures that the value in the input buffer is destroyed
1433+
// immediately rather than (potentially) arbitrarily later
1434+
// at a point where we want to put new values in the input buffer.
1435+
scope.pop();
1436+
1437+
// Push the cleanup to perform the reverse translation. This cleanup
1438+
// asserts ownership of the value of the temporary.
1439+
SGF.Cleanups.pushCleanup<TranslateIndirect>(outputOrigType,
1440+
outputSubstType,
1441+
inputOrigType,
1442+
inputSubstType,
1443+
temporaryAddr,
1444+
input.getLValueAddress());
1445+
1446+
// Add the temporary as an l-value argument.
1447+
Outputs.push_back(ManagedValue::forLValue(temporaryAddr));
1448+
return;
13661449
}
13671450
case ParameterConvention::Indirect_In: {
13681451
if (SGF.silConv.useLoweredAddresses()) {

test/SILGen/witnesses.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,37 @@ protocol EscapingReq {
522522
struct EscapingCovariance: EscapingReq {
523523
func f(_: (Int) -> Int) { }
524524
}
525+
526+
protocol InoutFunctionReq {
527+
associatedtype T
528+
func updateFunction(x: inout () -> T)
529+
}
530+
531+
// CHECK-LABEL: sil private [transparent] [thunk] @$S9witnesses13InoutFunctionVAA0bC3ReqA2aDP06updateC01xy1TQzycz_tFTW
532+
// CHECK: bb0(%0 : @trivial $*@callee_guaranteed () -> @out (), %1 : @trivial $*InoutFunction):
533+
// CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $@callee_guaranteed () -> ()
534+
// Reabstract the contents of the inout argument into the temporary.
535+
// CHECK-NEXT: [[OLD_FN:%.*]] = load [take] %0
536+
// CHECK-NEXT: // function_ref
537+
// CHECK-NEXT: [[THUNK:%.*]] = function_ref @$SytIegr_Ieg_TR
538+
// CHECK-NEXT: [[THUNKED_OLD_FN:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[OLD_FN]])
539+
// CHECK-NEXT: store [[THUNKED_OLD_FN]] to [init] [[TEMP]] :
540+
// Call the function.
541+
// CHECK-NEXT: [[SELF:%.*]] = load [trivial] %1 : $*InoutFunction
542+
// CHECK-NEXT: // function_ref
543+
// CHECK-NEXT: [[T0:%.*]] = function_ref @$S9witnesses13InoutFunctionV06updateC01xyyycz_tF :
544+
// CHECK-NEXT: apply [[T0]]([[TEMP]], [[SELF]])
545+
// CHECK-NEXT: [[TUPLE:%.*]] = tuple ()
546+
// Reabstract the contents of the temporary back into the inout argument.
547+
// CHECK-NEXT: [[NEW_FN:%.*]] = load [take] [[TEMP]]
548+
// CHECK-NEXT: // function_ref
549+
// CHECK-NEXT: [[THUNK:%.*]] = function_ref @$SIeg_ytIegr_TR
550+
// CHECK-NEXT: [[THUNKED_NEW_FN:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[NEW_FN]])
551+
// CHECK-NEXT: store [[THUNKED_NEW_FN]] to [init] %0 :
552+
// CHECK-NEXT: dealloc_stack [[TEMP]]
553+
// CHECK-NEXT: return [[TUPLE]]
554+
// CHECK-LABEL: } // end sil function '$S9witnesses13InoutFunctionVAA0bC3ReqA2aDP06updateC01xy1TQzycz_tFTW'
555+
556+
struct InoutFunction : InoutFunctionReq {
557+
func updateFunction(x: inout () -> ()) {}
558+
}

0 commit comments

Comments
 (0)