Skip to content

[5.5] [SILGen] Handle foreign funcs with error and async conventions. #38861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 7 additions & 12 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1689,11 +1689,6 @@ class DestructureInputs {
}

bool maybeAddForeignErrorParameter() {
// A foreign async convention absorbs any error parameter, making it into
// an argument to the callback.
if (Foreign.Async)
return false;

if (!Foreign.Error ||
NextOrigParamIndex != Foreign.Error->getErrorParameterIndex())
return false;
Expand Down Expand Up @@ -1764,12 +1759,12 @@ void updateResultTypeForForeignInfo(
if (!foreignInfo.Async && !foreignInfo.Error) {
return;
}
// A foreign async convention means our lowered return type is Void, since
// the imported semantic return and/or error type map to the completion
// callback's argument(s).
auto &C = substFormalResultType->getASTContext();
if (auto async = foreignInfo.Async) {

// A foreign async convention without an error convention means our lowered
// return type is Void, since the imported semantic return map to the
// completion callback's argument(s).
if (!foreignInfo.Error) {
auto &C = substFormalResultType->getASTContext();
substFormalResultType = TupleType::getEmpty(C);
origResultType = AbstractionPattern(genericSig, substFormalResultType);
return;
Expand All @@ -1781,7 +1776,7 @@ void updateResultTypeForForeignInfo(
// These conventions replace the result type.
case ForeignErrorConvention::ZeroResult:
case ForeignErrorConvention::NonZeroResult:
assert(substFormalResultType->isVoid());
assert(substFormalResultType->isVoid() || foreignInfo.Async);
substFormalResultType = convention.getResultType();
origResultType = AbstractionPattern(genericSig, substFormalResultType);
return;
Expand Down
169 changes: 138 additions & 31 deletions lib/SILGen/ResultPlan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class InPlaceInitializationResultPlan final : public ResultPlan {
InPlaceInitializationResultPlan(Initialization *init) : init(init) {}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
init->finishInitialization(SGF);
return RValue::forInContext();
}
Expand Down Expand Up @@ -169,7 +170,8 @@ class IndirectOpenedSelfResultPlan final : public ResultPlan {
}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
assert(resultBox && "never emitted temporary?!");

// Lower the unabstracted result type.
Expand Down Expand Up @@ -209,7 +211,8 @@ class ScalarResultPlan final : public ResultPlan {
rep(rep) {}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
// Lower the unabstracted result type.
auto &substTL = SGF.getTypeLowering(substType);

Expand Down Expand Up @@ -309,8 +312,10 @@ class InitValueFromTemporaryResultPlan final : public ResultPlan {
temporary(std::move(temporary)) {}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
RValue subResult = subPlan->finish(SGF, loc, substType, directResults);
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
RValue subResult = subPlan->finish(SGF, loc, substType, directResults,
bridgedForeignError);
assert(subResult.isInContext() && "sub-plan didn't emit into context?");
(void)subResult;

Expand Down Expand Up @@ -339,8 +344,10 @@ class InitValueFromRValueResultPlan final : public ResultPlan {
: init(init), subPlan(std::move(subPlan)) {}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
RValue subResult = subPlan->finish(SGF, loc, substType, directResults);
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
RValue subResult = subPlan->finish(SGF, loc, substType, directResults,
bridgedForeignError);
ManagedValue value = std::move(subResult).getAsSingleValue(SGF, loc);

init->copyOrInitValueInto(SGF, loc, value, /*init*/ true);
Expand Down Expand Up @@ -374,15 +381,17 @@ class TupleRValueResultPlan final : public ResultPlan {
}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
RValue tupleRV(substType);

// Finish all the component tuples.
auto substTupleType = cast<TupleType>(substType);
assert(substTupleType.getElementTypes().size() == eltPlans.size());
for (auto i : indices(substTupleType.getElementTypes())) {
RValue eltRV = eltPlans[i]->finish(
SGF, loc, substTupleType.getElementType(i), directResults);
RValue eltRV =
eltPlans[i]->finish(SGF, loc, substTupleType.getElementType(i),
directResults, bridgedForeignError);
tupleRV.addElement(std::move(eltRV));
}

Expand Down Expand Up @@ -428,12 +437,14 @@ class TupleInitializationResultPlan final : public ResultPlan {
}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
auto substTupleType = cast<TupleType>(substType);
assert(substTupleType.getElementTypes().size() == eltPlans.size());
for (auto i : indices(substTupleType.getElementTypes())) {
auto eltType = substTupleType.getElementType(i);
RValue eltRV = eltPlans[i]->finish(SGF, loc, eltType, directResults);
RValue eltRV = eltPlans[i]->finish(SGF, loc, eltType, directResults,
bridgedForeignError);
assert(eltRV.isInContext());
(void)eltRV;
}
Expand Down Expand Up @@ -482,9 +493,11 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
AbstractionPattern origFormalType,
SILLocation loc) override {
// Get the current continuation for the task.
bool throws = calleeTypeInfo.foreign.async
->completionHandlerErrorParamIndex().hasValue();

bool throws =
calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex()
.hasValue() ||
calleeTypeInfo.foreign.error.hasValue();

continuation = SGF.B.createGetAsyncContinuationAddr(loc, resumeBuf,
calleeTypeInfo.substResultType, throws);

Expand Down Expand Up @@ -534,7 +547,8 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
cast<SILFunctionType>(
impFnTy->mapTypeOutOfContext()->getCanonicalType(sig)),
continuationTy->mapTypeOutOfContext()->getCanonicalType(sig),
origFormalType, sig, *calleeTypeInfo.foreign.async);
origFormalType, sig, *calleeTypeInfo.foreign.async,
calleeTypeInfo.foreign.error);
auto impRef = SGF.B.createFunctionRef(loc, impl);

// Initialize the block object for the completion handler.
Expand All @@ -554,18 +568,95 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
// There should be no direct results from the call.
assert(directResults.empty());

// Await the continuation we handed off to the completion handler.
SILBasicBlock *resumeBlock = SGF.createBasicBlock();
SILBasicBlock *errorBlock = nullptr;
auto errorParamIndex = calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex();
if (errorParamIndex) {
bool throws =
calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex()
.hasValue() ||
calleeTypeInfo.foreign.error.hasValue();
if (throws) {
errorBlock = SGF.createBasicBlock(FunctionSection::Postmatter);
}


auto *awaitBB = SGF.B.getInsertionBB();
if (bridgedForeignError) {
// Avoid a critical edge from the block which branches to the await and
// foreign error blocks to the await block (to which the error block will
// be made to branch in a moment) by introducing a trampoline which will
// branch to the await block.
awaitBB = SGF.createBasicBlock();
SGF.B.createBranch(loc, awaitBB);

// Finish emitting the foreign error block:
// (1) fulfill the unsafe continuation with the foreign error
// (2) branch to the await block
{
// First, fulfill the unsafe continuation with the foreign error.
// Currently, that block's code looks something like
// %foreignError = ... : $*Optional<NSError>
// %converter = function_ref _convertNSErrorToError(_:)
// %error = apply %converter(%foreignError)
// [... insert here ...]
// destroy_value %error
// destroy_value %foreignError
// Insert code to fulfill it after the native %error is defined. That
// code should structure the RawUnsafeContinuation (continuation) into
// an appropriately typed UnsafeContinuation and then pass that together
// with (a copy of) the error to
// _resumeUnsafeThrowingContinuationWithError.
// [foreign_error_block_with_foreign_async_convention]
SGF.B.setInsertionPoint(
++bridgedForeignError->getDefiningInstruction()->getIterator());

auto continuationDecl = SGF.getASTContext().getUnsafeContinuationDecl();

auto errorTy = SGF.getASTContext().getExceptionType();
auto continuationBGT =
BoundGenericType::get(continuationDecl, Type(),
{calleeTypeInfo.substResultType, errorTy});
auto env = SGF.F.getGenericEnvironment();
auto sig = env ? env->getGenericSignature().getCanonicalSignature()
: CanGenericSignature();
auto mappedContinuationTy =
continuationBGT->mapTypeOutOfContext()->getCanonicalType(sig);
auto resumeType =
cast<BoundGenericType>(mappedContinuationTy).getGenericArgs()[0];
auto continuationTy = continuationBGT->getCanonicalType();

auto errorIntrinsic =
SGF.SGM.getResumeUnsafeThrowingContinuationWithError();
Type replacementTypes[] = {
SGF.F.mapTypeIntoContext(resumeType)->getCanonicalType()};
auto subs = SubstitutionMap::get(errorIntrinsic->getGenericSignature(),
replacementTypes,
ArrayRef<ProtocolConformanceRef>{});
auto wrappedContinuation = SGF.B.createStruct(
loc, SILType::getPrimitiveObjectType(continuationTy),
{continuation});

auto continuationMV =
ManagedValue::forUnmanaged(SILValue(wrappedContinuation));
SGF.emitApplyOfLibraryIntrinsic(
loc, errorIntrinsic, subs,
{continuationMV,
ManagedValue::forUnmanaged(bridgedForeignError).copy(SGF, loc)},
SGFContext());

// Second, emit a branch from the end of the foreign error block to the
// await block, to await the continuation which was just fulfilled.
SGF.B.setInsertionPoint(
bridgedForeignError->getDefiningInstruction()->getParent());
SGF.B.createBranch(loc, awaitBB);
}

SGF.B.emitBlock(awaitBB);
}
SGF.B.createAwaitAsyncContinuation(loc, continuation, resumeBlock, errorBlock);

// Propagate an error if we have one.
Expand Down Expand Up @@ -655,8 +746,10 @@ class ForeignErrorInitializationPlan final : public ResultPlan {
}

RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) override {
return subPlan->finish(SGF, loc, substType, directResults);
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) override {
return subPlan->finish(SGF, loc, substType, directResults,
bridgedForeignError);
}

void
Expand All @@ -665,6 +758,13 @@ class ForeignErrorInitializationPlan final : public ResultPlan {
subPlan->gatherIndirectResultAddrs(SGF, loc, outList);
}

ManagedValue
emitForeignAsyncCompletionHandler(SILGenFunction &SGF,
AbstractionPattern origFormalType,
SILLocation loc) override {
return subPlan->emitForeignAsyncCompletionHandler(SGF, origFormalType, loc);
}

Optional<std::pair<ManagedValue, ManagedValue>>
emitForeignErrorArgument(SILGenFunction &SGF, SILLocation loc) override {
SILGenFunction::PointerAccessInfo pointerInfo = {
Expand Down Expand Up @@ -695,12 +795,7 @@ class ForeignErrorInitializationPlan final : public ResultPlan {
ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init,
SILLocation loc) {
// First check if we have a foreign error and/or async convention.
if (auto foreignAsync = calleeTypeInfo.foreign.async) {
// Create a result plan that gets the result schema from the completion
// handler callback's arguments.
// completion handler.
return ResultPlanPtr(new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo));
} else if (auto foreignError = calleeTypeInfo.foreign.error) {
if (auto foreignError = calleeTypeInfo.foreign.error) {
// Handle the foreign error first.
//
// The plan needs to be built using the formal result type after foreign-error
Expand All @@ -709,7 +804,8 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init,
// These conventions make the formal result type ().
case ForeignErrorConvention::ZeroResult:
case ForeignErrorConvention::NonZeroResult:
assert(calleeTypeInfo.substResultType->isVoid());
assert(calleeTypeInfo.substResultType->isVoid() ||
calleeTypeInfo.foreign.async);
allResults.clear();
break;

Expand All @@ -734,10 +830,21 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init,
}
}

ResultPlanPtr subPlan = build(init, calleeTypeInfo.origResultType.getValue(),
calleeTypeInfo.substResultType);
ResultPlanPtr subPlan;
if (auto foreignAsync = calleeTypeInfo.foreign.async) {
subPlan = ResultPlanPtr(
new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo));
} else {
subPlan = build(init, calleeTypeInfo.origResultType.getValue(),
calleeTypeInfo.substResultType);
}
return ResultPlanPtr(new ForeignErrorInitializationPlan(
SGF, loc, calleeTypeInfo, std::move(subPlan)));
} else if (auto foreignAsync = calleeTypeInfo.foreign.async) {
// Create a result plan that gets the result schema from the completion
// handler callback's arguments.
return ResultPlanPtr(
new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo));
} else {
// Otherwise, we can just call build.
return build(init, calleeTypeInfo.origResultType.getValue(),
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/ResultPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class CalleeTypeInfo;
class ResultPlan {
public:
virtual RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
ArrayRef<ManagedValue> &directResults) = 0;
ArrayRef<ManagedValue> &directResults,
SILValue bridgedForeignError) = 0;
virtual ~ResultPlan() = default;

virtual void
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
SILFunction *getOrCreateForeignAsyncCompletionHandlerImplFunction(
CanSILFunctionType blockType, CanType continuationTy,
AbstractionPattern origFormalType, CanGenericSignature sig,
ForeignAsyncConvention convention);
ForeignAsyncConvention convention,
Optional<ForeignErrorConvention> foreignError);

/// Determine whether the given class has any instance variables that
/// need to be destroyed.
Expand Down
Loading