Skip to content

[Typed throws] Implement reabstraction thunks that change the error #69700

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
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
5 changes: 1 addition & 4 deletions include/swift/SIL/SILFunctionConventions.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,7 @@ class SILFunctionConventions {
return getSILType(funcTy->getErrorResult(), context);
}

bool isTypedError() const {
return !funcTy->getErrorResult()
.getInterfaceType()->isExistentialWithError();
}
bool isTypedError() const;

/// Returns an array of result info.
/// Provides convenient access to the underlying SILFunctionType.
Expand Down
11 changes: 9 additions & 2 deletions lib/SIL/IR/AbstractionPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1279,8 +1279,15 @@ AbstractionPattern::getFunctionThrownErrorType(
(*optErrorType)->getCanonicalType());
}

if (!optErrorType)
optErrorType = ctx.getErrorExistentialType();
if (!optErrorType) {
Type origErrorSubstType =
optOrigErrorType->getType()
.subst(optOrigErrorType->getGenericSubstitutions());
if (origErrorSubstType->isNever())
optErrorType = ctx.getNeverType();
else
optErrorType = ctx.getErrorExistentialType();
}

return std::make_pair(*optOrigErrorType,
(*optErrorType)->getCanonicalType());
Expand Down
7 changes: 7 additions & 0 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4943,3 +4943,10 @@ CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext(
auto funTy = M.Types.getLoweredType(origFunTy , context);
return cast<SILFunctionType>(funTy.getASTType());
}

bool SILFunctionConventions::isTypedError() const {
return !funcTy->getErrorResult()
.getInterfaceType()->isEqual(
funcTy->getASTContext().getErrorExistentialType()) ||
hasIndirectSILErrorResults();
}
1 change: 0 additions & 1 deletion lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6071,7 +6071,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
}

if (fnConv.hasIndirectSILErrorResults()) {
assert(fnConv.isTypedError());
auto errorResult = fnConv.getSILErrorType(F.getTypeExpansionContext());
check("indirect error result", errorResult);
}
Expand Down
60 changes: 52 additions & 8 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5715,7 +5715,6 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
CanSILFunctionType silFnType = substFnType.castTo<SILFunctionType>();
SILFunctionConventions fnConv(silFnType, SGM.M);
SILType resultType = fnConv.getSILResultType(getTypeExpansionContext());

if (!silFnType->hasErrorResult()) {
return B.createApply(loc, fn, subs, args);
}
Expand All @@ -5728,19 +5727,64 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
{
B.emitBlock(errorBB);

SILValue error;
bool indirectError = fnConv.hasIndirectSILErrorResults();

if (!indirectError) {
error = errorBB->createPhiArgument(
// Grab the inner error.
SILValue innerError;
bool hasInnerIndirectError = fnConv.hasIndirectSILErrorResults();
if (!hasInnerIndirectError) {
innerError = errorBB->createPhiArgument(
fnConv.getSILErrorType(getTypeExpansionContext()),
OwnershipKind::Owned);
} else {
// FIXME: This probably belongs on SILFunctionConventions.
innerError = args[fnConv.getNumIndirectSILResults()];
}

// Convert to the outer error, if we need to.
SILValue outerError;
SILType innerErrorType = innerError->getType().getObjectType();
SILType outerErrorType = F.mapTypeIntoContext(
F.getConventions().getSILErrorType(getTypeExpansionContext()));
if (IndirectErrorResult && IndirectErrorResult == innerError) {
// Fast path: we aliased the indirect error result slot because both are
// indirect and the types matched, so we are done.
} else if (!IndirectErrorResult && !hasInnerIndirectError &&
innerErrorType == outerErrorType) {
// Fast path: both have a direct error result and the types line up, so
// rethrow the inner error.
outerError = innerError;
} else {
// The error requires some kind of translation.

// Load the inner error, if it was returned indirectly.
if (innerError->getType().isAddress()) {
innerError = emitLoad(loc, innerError, getTypeLowering(innerErrorType),
SGFContext(), IsTake).forward(*this);
}

// If we need to convert the error type, do so now.
if (innerErrorType != outerErrorType) {
auto conversion = Conversion::getOrigToSubst(
AbstractionPattern(innerErrorType.getASTType()),
outerErrorType.getASTType(),
outerErrorType);
outerError = emitConvertedRValue(loc, conversion, SGFContext(),
[innerError](SILGenFunction &SGF, SILLocation loc, SGFContext C) {
return ManagedValue::forForwardedRValue(SGF, innerError);
}).forward(*this);
}

// If the outer error is returned indirectly, copy from the converted
// inner error to the outer error slot.
if (IndirectErrorResult) {
emitSemanticStore(loc, outerError, IndirectErrorResult,
getTypeLowering(outerErrorType), IsInitialization);
}
}

Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind);

if (!indirectError)
B.createThrow(loc, error);
if (!IndirectErrorResult)
B.createThrow(loc, outerError);
else
B.createThrowAddr(loc);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2273,7 +2273,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// Used for emitting SILArguments of bare functions, such as thunks.
void collectThunkParams(
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr);
SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr,
SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr);

/// Build the type of a function transformation thunk.
CanSILFunctionType buildThunkType(CanSILFunctionType &sourceType,
Expand Down
41 changes: 40 additions & 1 deletion lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,8 @@ ManagedValue Transform::transformTuple(ManagedValue inputTuple,

void SILGenFunction::collectThunkParams(
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
SmallVectorImpl<ManagedValue> *indirectResults) {
SmallVectorImpl<ManagedValue> *indirectResults,
SmallVectorImpl<ManagedValue> *indirectErrors) {
// Add the indirect results.
for (auto resultTy : F.getConventions().getIndirectSILResultTypes(
getTypeExpansionContext())) {
Expand All @@ -864,6 +865,19 @@ void SILGenFunction::collectThunkParams(
indirectResults->push_back(ManagedValue::forLValue(arg));
}

if (F.getConventions().hasIndirectSILErrorResults()) {
assert(F.getConventions().getNumIndirectSILErrorResults() == 1);
auto paramTy = F.mapTypeIntoContext(
F.getConventions().getSILErrorType(getTypeExpansionContext()));
auto inContextParamTy = F.getLoweredType(paramTy.getASTType())
.getCategoryType(paramTy.getCategory());
SILArgument *arg = F.begin()->createFunctionArgument(inContextParamTy);
if (indirectErrors)
indirectErrors->push_back(ManagedValue::forLValue(arg));

IndirectErrorResult = arg;
}

// Add the parameters.
auto paramTypes = F.getLoweredFunctionType()->getParameters();
for (auto param : paramTypes) {
Expand Down Expand Up @@ -4831,6 +4845,31 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
outputSubstType.getResult(),
fnType, thunkType);

// If the function we're calling has as indirect error result, create an
// argument for it.
SILValue innerIndirectErrorAddr;
if (auto innerError = fnType->getOptionalErrorResult()) {
if (innerError->getConvention() == ResultConvention::Indirect) {
auto loweredErrorResultType = SGF.getSILType(*innerError, fnType);
if (SGF.IndirectErrorResult &&
SGF.IndirectErrorResult->getType().getObjectType()
== loweredErrorResultType) {
// The type of the indirect error is the same for both the inner
// function and the thunk, so we can re-use the indirect error slot.
innerIndirectErrorAddr = SGF.IndirectErrorResult;
} else {
// The type of the indirect error in the inner function differs from
// that of the thunk, or the thunk has a direct error, so allocate a
// stack location for the inner indirect error.
innerIndirectErrorAddr =
SGF.B.createAllocStack(loc, loweredErrorResultType);
SGF.enterDeallocStackCleanup(innerIndirectErrorAddr);
}

argValues.push_back(innerIndirectErrorAddr);
}
}

// Add the rest of the arguments.
forwardFunctionArguments(SGF, loc, fnType, args, argValues);

Expand Down
23 changes: 14 additions & 9 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1813,17 +1813,22 @@ uint16_t SILGenFunction::emitBasicProlog(
emitIndirectResultParameters(*this, resultType, origResultType, DC);

llvm::Optional<AbstractionPattern> origErrorType;
if (errorType) {
if (origClosureType &&
!origClosureType->isTypeParameterOrOpaqueArchetype()) {
origErrorType =
origClosureType->getFunctionThrownErrorType();
} else {
origErrorType =
AbstractionPattern(genericSig.getCanonicalSignature(),
(*errorType)->getCanonicalType());
if (origClosureType && !origClosureType->isTypeParameterOrOpaqueArchetype()) {
CanType substClosureType = origClosureType->getType()
.subst(origClosureType->getGenericSubstitutions())->getCanonicalType();
CanAnyFunctionType substClosureFnType =
cast<AnyFunctionType>(substClosureType);
if (auto optPair = origClosureType->getFunctionThrownErrorType(substClosureFnType)) {
origErrorType = optPair->first;
errorType = optPair->second;
}
} else if (errorType) {
origErrorType = AbstractionPattern(genericSig.getCanonicalSignature(),
(*errorType)->getCanonicalType());
}

if (origErrorType && errorType &&
F.getConventions().hasIndirectSILErrorResults()) {
emitIndirectErrorParameter(*this, *errorType, *origErrorType, DC);
}

Expand Down
99 changes: 53 additions & 46 deletions lib/SILGen/SILGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1547,58 +1547,65 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
}
}

bool shouldDiscard = ThrowDest.getThrownError().Discard;
SILType exnType = exn->getType().getObjectType();
SILBasicBlock &throwBB = *ThrowDest.getBlock();
if (!throwBB.getArguments().empty()) {
assert(exn);
assert(!indirectErrorAddr);

auto errorArg = throwBB.getArguments()[0];

// If we don't have an existential box, create one to jump to the throw
// destination.
SILType errorArgType = errorArg->getType();
if (exn->getType() != errorArgType) {
SILType existentialBoxType = SILType::getExceptionType(getASTContext());
assert(errorArgType == existentialBoxType);

// FIXME: Callers should provide this conformance from places recorded in
// the AST.
ProtocolConformanceRef conformances[1] = {
getModule().getSwiftModule()->conformsToProtocol(
exn->getType().getASTType(), getASTContext().getErrorDecl())
};
exnMV = emitExistentialErasure(
loc,
exn->getType().getASTType(),
getTypeLowering(exn->getType()),
getTypeLowering(existentialBoxType),
getASTContext().AllocateCopy(conformances),
SGFContext(),
[&](SGFContext C) -> ManagedValue {
return ManagedValue::forForwardedRValue(*this, exn);
});

exn = exnMV.forward(*this);
}

// A direct error value is passed to the epilog block as a BB argument.
args.push_back(exn);
} else if (ThrowDest.getThrownError().Discard) {
assert(!indirectErrorAddr);
if (exn)
B.createDestroyAddr(loc, exn);
} else {
assert(indirectErrorAddr);
SILType destErrorType = indirectErrorAddr
? indirectErrorAddr->getType().getObjectType()
: !throwBB.getArguments().empty()
? throwBB.getArguments()[0]->getType().getObjectType()
: exnType;

// If the thrown error type differs from what the throw destination expects,
// perform the conversion.
// FIXME: Can the AST tell us what to do here?
if (exnType != destErrorType && !shouldDiscard) {
assert(destErrorType == SILType::getExceptionType(getASTContext()));

ProtocolConformanceRef conformances[1] = {
getModule().getSwiftModule()->conformsToProtocol(
exn->getType().getASTType(), getASTContext().getErrorDecl())
};

exn = emitExistentialErasure(
loc,
exnType.getASTType(),
getTypeLowering(exnType),
getTypeLowering(destErrorType),
getASTContext().AllocateCopy(conformances),
SGFContext(),
[&](SGFContext C) -> ManagedValue {
if (exn->getType().isAddress()) {
return emitLoad(loc, exn, getTypeLowering(exnType), SGFContext(),
IsTake);
}

// FIXME: opaque values
return ManagedValue::forForwardedRValue(*this, exn);
}).forward(*this);
}
assert(exn->getType().getObjectType() == destErrorType);

// If an error value was provided by the caller, copy it into the
// indirect error result. Otherwise we assume the indirect error
// result has been initialized.
if (exn) {
if (indirectErrorAddr) {
if (exn->getType().isAddress()) {
B.createCopyAddr(loc, exn, indirectErrorAddr,
IsTake, IsInitialization);
} else {
// An indirect error is written into the destination error address.
emitSemanticStore(loc, exn, indirectErrorAddr,
getTypeLowering(destErrorType), IsInitialization);
}
} else if (!throwBB.getArguments().empty()) {
// Load if we need to.
if (exn->getType().isAddress()) {
exn = emitLoad(loc, exn, getTypeLowering(exnType), SGFContext(), IsTake)
.forward(*this);
}

// A direct error value is passed to the epilog block as a BB argument.
args.push_back(exn);
} else if (shouldDiscard) {
if (exn && exn->getType().isAddress())
B.createDestroyAddr(loc, exn);
}

// Branch to the cleanup destination.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ static bool canSpecializeFunction(SILFunction *F,
if (F->getConventions().hasIndirectSILResults())
return false;

// For now ignore functions with indirect error results.
if (F->getConventions().hasIndirectSILErrorResults())
return false;

// For now ignore coroutines.
if (F->getLoweredFunctionType()->isCoroutine())
return false;
Expand Down
4 changes: 4 additions & 0 deletions lib/SILOptimizer/Utils/Generics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,10 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee,
if (shouldNotSpecialize(Callee, Apply ? Apply.getFunction() : nullptr))
return false;

// FIXME: Specialization with typed throws.
if (Callee->getConventions().hasIndirectSILErrorResults())
return false;

SpecializedGenericEnv = nullptr;
SpecializedGenericSig = nullptr;
auto CalleeGenericSig = Callee->getLoweredFunctionType()
Expand Down
Loading