Skip to content

Commit b6b999a

Browse files
committed
[Typed throws] Basic SIL lowering and SIL generation for typed throws
Lower the thrown error type into the SIL function type. This requires very little code because the thrown error type was already modeled as a SILResultInfo, which carries type information. Note that this lowering does not yet account for error types that need to passed indirectly, but we will need to do so for (e.g.) using resilient error types. Teach a few places in SIL generation not to assume that thrown types are always the existential error type, which primarily comes down to ensuring that rethrow epilogues have the thrown type of the corresponding function or closure. Teach throw emission to implicitly box concrete thrown errors in the error existential when needed to satisfy the throw destination. This is a temporary solution that helps translate typed throws into untyped throws, but it should be replaced by a better modeling within the AST of the points at which thrown errors are converted.
1 parent 31b8811 commit b6b999a

18 files changed

+232
-34
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7066,6 +7066,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
70667066
/// Retrieves the thrown interface type.
70677067
Type getThrownInterfaceType() const;
70687068

7069+
/// Retrieve the "effective" thrown interface type, or llvm::None if
7070+
/// this function cannot throw.
7071+
///
7072+
/// Functions with untyped throws will produce "any Error", functions that
7073+
/// cannot throw or are specified to throw "Never" will return llvm::None.
7074+
llvm::Optional<Type> getEffectiveThrownInterfaceType() const;
7075+
70697076
/// Returns if the function throws or is async.
70707077
bool hasEffect(EffectKind kind) const;
70717078

include/swift/AST/Expr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3847,6 +3847,13 @@ class AbstractClosureExpr : public DeclContext, public Expr {
38473847
/// Return whether this closure is throwing when fully applied.
38483848
bool isBodyThrowing() const;
38493849

3850+
/// Retrieve the "effective" thrown interface type, or llvm::None if
3851+
/// this closure cannot throw.
3852+
///
3853+
/// Closures with untyped throws will produce "any Error", functions that
3854+
/// cannot throw or are specified to throw "Never" will return llvm::None.
3855+
llvm::Optional<Type> getEffectiveThrownType() const;
3856+
38503857
/// \brief Return whether this closure is async when fully applied.
38513858
bool isBodyAsync() const;
38523859

include/swift/AST/Types.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3386,6 +3386,13 @@ class AnyFunctionType : public TypeBase {
33863386
Type getGlobalActor() const;
33873387
Type getThrownError() const;
33883388

3389+
/// Retrieve the "effective" thrown interface type, or llvm::None if
3390+
/// this function cannot throw.
3391+
///
3392+
/// Functions with untyped throws will produce "any Error", functions that
3393+
/// cannot throw or are specified to throw "Never" will return llvm::None.
3394+
llvm::Optional<Type> getEffectiveThrownInterfaceType() const;
3395+
33893396
/// Returns true if the function type stores a Clang type that cannot
33903397
/// be derived from its Swift type. Returns false otherwise, including if
33913398
/// the function type is not @convention(c) or @convention(block).
@@ -3612,7 +3619,8 @@ BEGIN_CAN_TYPE_WRAPPER(AnyFunctionType, Type)
36123619
}
36133620

36143621
PROXY_CAN_TYPE_SIMPLE_GETTER(getResult)
3615-
3622+
PROXY_CAN_TYPE_SIMPLE_GETTER(getThrownError)
3623+
36163624
CanAnyFunctionType withExtInfo(ExtInfo info) const {
36173625
return CanAnyFunctionType(getPointer()->withExtInfo(info));
36183626
}

lib/AST/ASTVerifier.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,9 +1014,15 @@ class Verifier : public ASTWalker {
10141014
}
10151015

10161016
void verifyChecked(ThrowStmt *S) {
1017-
checkSameType(S->getSubExpr()->getType(),
1018-
checkExceptionTypeExists("throw expression"),
1019-
"throw operand");
1017+
Type thrownError;
1018+
if (!Functions.empty()) {
1019+
if (auto fn = AnyFunctionRef::fromDeclContext(Functions.back()))
1020+
thrownError = fn->getThrownErrorType();
1021+
}
1022+
1023+
if (!thrownError)
1024+
thrownError = checkExceptionTypeExists("throw expression");
1025+
checkSameType(S->getSubExpr()->getType(), thrownError, "throw operand");
10201026
verifyCheckedBase(S);
10211027
}
10221028

lib/AST/Decl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,18 @@ Type AbstractFunctionDecl::getThrownInterfaceType() const {
952952
Type());
953953
}
954954

955+
llvm::Optional<Type>
956+
AbstractFunctionDecl::getEffectiveThrownInterfaceType() const {
957+
Type interfaceType = getInterfaceType();
958+
if (hasImplicitSelfDecl()) {
959+
if (auto fnType = interfaceType->getAs<AnyFunctionType>())
960+
interfaceType = fnType->getResult();
961+
}
962+
963+
return interfaceType->castTo<AnyFunctionType>()
964+
->getEffectiveThrownInterfaceType();
965+
}
966+
955967
Expr *AbstractFunctionDecl::getSingleExpressionBody() const {
956968
assert(hasSingleExpressionBody() && "Not a single-expression body");
957969
auto braceStmt = getBody();

lib/AST/Expr.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,11 @@ Type AbstractClosureExpr::getResultType(
19301930
return T->castTo<FunctionType>()->getResult();
19311931
}
19321932

1933+
llvm::Optional<Type> AbstractClosureExpr::getEffectiveThrownType() const {
1934+
return getType()->castTo<AnyFunctionType>()
1935+
->getEffectiveThrownInterfaceType();
1936+
}
1937+
19331938
bool AbstractClosureExpr::isBodyThrowing() const {
19341939
if (!getType() || getType()->hasError()) {
19351940
// Scan the closure body to infer effects.

lib/AST/Type.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5419,6 +5419,24 @@ AnyFunctionType *AnyFunctionType::getWithoutThrowing() const {
54195419
return withExtInfo(info);
54205420
}
54215421

5422+
llvm::Optional<Type> AnyFunctionType::getEffectiveThrownInterfaceType() const {
5423+
// A non-throwing function... has no thrown interface type.
5424+
if (!isThrowing())
5425+
return llvm::None;
5426+
5427+
// If there is no specified thrown error type, it throws "any Error".
5428+
Type thrownError = getThrownError();
5429+
if (!thrownError)
5430+
return getASTContext().getErrorExistentialType();
5431+
5432+
// If the thrown interface type is "Never", this function does not throw.
5433+
if (thrownError->isEqual(getASTContext().getNeverType()))
5434+
return llvm::None;
5435+
5436+
// Otherwise, return the typed error.
5437+
return thrownError;
5438+
}
5439+
54225440
llvm::Optional<TangentSpace>
54235441
TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) {
54245442
assert(lookupConformance);

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2202,7 +2202,13 @@ static CanSILFunctionType getSILFunctionType(
22022202
!foreignInfo.async) {
22032203
assert(!origType.isForeign()
22042204
&& "using native Swift error convention for foreign type!");
2205-
SILType exnType = SILType::getExceptionType(TC.Context);
2205+
SILType exnType;
2206+
if (CanType thrownError = substFnInterfaceType.getThrownError()) {
2207+
exnType = TC.getLoweredType(thrownError, expansionContext);
2208+
} else {
2209+
// Untyped error throws the exception type.
2210+
exnType = SILType::getExceptionType(TC.Context);
2211+
}
22062212
assert(exnType.isObject());
22072213
errorResult = SILResultInfo(exnType.getASTType(),
22082214
ResultConvention::Owned);

lib/SILGen/SILGenBackDeploy.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
237237
paramsForForwarding.emplace_back(param.forward(*this));
238238
}
239239

240-
prepareEpilog(getResultInterfaceType(AFD), AFD->hasThrows(),
240+
prepareEpilog(getResultInterfaceType(AFD),
241+
AFD->getEffectiveThrownInterfaceType(),
241242
CleanupLocation(AFD));
242243

243244
SILBasicBlock *availableBB = createBasicBlock("availableBB");

lib/SILGen/SILGenBridging.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,15 +2081,15 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
20812081
auto foreignFnTy = foreignCI.SILFnType;
20822082

20832083
// Find the foreign error/async convention and 'self' parameter index.
2084-
bool hasError = false;
2084+
llvm::Optional<Type> thrownErrorType;
20852085
llvm::Optional<ForeignAsyncConvention> foreignAsync;
20862086
if (nativeFnTy->isAsync()) {
20872087
foreignAsync = fd->getForeignAsyncConvention();
20882088
assert(foreignAsync && "couldn't find foreign async convention?!");
20892089
}
20902090
llvm::Optional<ForeignErrorConvention> foreignError;
20912091
if (nativeFnTy->hasErrorResult()) {
2092-
hasError = true;
2092+
thrownErrorType = nativeFnTy->getErrorResult().getInterfaceType();
20932093
foreignError = fd->getForeignErrorConvention();
20942094
assert((foreignError || foreignAsync)
20952095
&& "couldn't find foreign error or async convention for foreign error!");
@@ -2133,8 +2133,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
21332133

21342134
// Set up the throw destination if necessary.
21352135
CleanupLocation cleanupLoc(fd);
2136-
if (hasError) {
2137-
prepareRethrowEpilog(cleanupLoc);
2136+
if (thrownErrorType) {
2137+
prepareRethrowEpilog(*thrownErrorType, cleanupLoc);
21382138
}
21392139

21402140
SILValue result;

0 commit comments

Comments
 (0)