Skip to content

Commit 0b0c4e2

Browse files
committed
[Typed throws] Enable checking of thrown types on closures.
Enable typed throws on explicit closures, either due to contextual type information or due to explicit specification.
1 parent 166f929 commit 0b0c4e2

File tree

7 files changed

+123
-6
lines changed

7 files changed

+123
-6
lines changed

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ SIMPLE_LOCATOR_PATH_ELT(AutoclosureResult)
5454
/// The result of a closure.
5555
SIMPLE_LOCATOR_PATH_ELT(ClosureResult)
5656

57+
/// The thrown error of a closure.
58+
SIMPLE_LOCATOR_PATH_ELT(ClosureThrownError)
59+
5760
/// FIXME: Misleading name: this locator is used only for single-expression
5861
/// closure returns.
5962
///

lib/Sema/CSGen.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,6 +2454,49 @@ namespace {
24542454
auto resultLocator =
24552455
CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult);
24562456

2457+
// FIXME: Need a better locator.
2458+
auto thrownErrorLocator =
2459+
CS.getConstraintLocator(closure, ConstraintLocator::ClosureThrownError);
2460+
2461+
// Determine the thrown error type, when appropriate.
2462+
Type thrownErrorTy = [&] {
2463+
// Explicitly-specified thrown type.
2464+
if (auto thrownTypeRepr = closure->getExplicitThrownTypeRepr()) {
2465+
Type resolvedTy = resolveTypeReferenceInExpression(
2466+
thrownTypeRepr, TypeResolverContext::InExpression,
2467+
thrownErrorLocator);
2468+
if (resolvedTy)
2469+
return resolvedTy;
2470+
}
2471+
2472+
// Thrown type inferred from context.
2473+
if (auto contextualType = CS.getContextualType(
2474+
closure, /*forConstraint=*/false)) {
2475+
if (auto fnType = contextualType->getAs<AnyFunctionType>()) {
2476+
if (Type thrownErrorTy = fnType->getThrownError())
2477+
return thrownErrorTy;
2478+
}
2479+
}
2480+
2481+
// We do not try to infer a thrown error type if one isn't immediately
2482+
// available. We could attempt this in the future.
2483+
return Type();
2484+
}();
2485+
2486+
if (thrownErrorTy) {
2487+
// Record the thrown error type in the extended info for the function
2488+
// type of the closure.
2489+
extInfo = extInfo.withThrows(true, thrownErrorTy);
2490+
2491+
// Ensure that the thrown error type conforms to Error.
2492+
if (auto errorProto =
2493+
CS.getASTContext().getProtocol(KnownProtocolKind::Error)) {
2494+
CS.addConstraint(
2495+
ConstraintKind::ConformsTo, thrownErrorTy,
2496+
errorProto->getDeclaredInterfaceType(), thrownErrorLocator);
2497+
}
2498+
}
2499+
24572500
// Closure expressions always have function type. In cases where a
24582501
// parameter or return type is omitted, a fresh type variable is used to
24592502
// stand in for that parameter or return type, allowing it to be inferred

lib/Sema/CSSyntacticElement.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -908,12 +908,18 @@ class SyntacticElementConstraintGenerator
908908
}
909909

910910
void visitThrowStmt(ThrowStmt *throwStmt) {
911-
if (!cs.getASTContext().getErrorDecl()) {
912-
hadError = true;
913-
return;
911+
912+
// Find the thrown type of our current context.
913+
Type errType = getContextualThrownErrorType();
914+
if (!errType) {
915+
if (!cs.getASTContext().getErrorDecl()) {
916+
hadError = true;
917+
return;
918+
}
919+
920+
errType = cs.getASTContext().getErrorExistentialType();
914921
}
915922

916-
auto errType = cs.getASTContext().getErrorExistentialType();
917923
auto *errorExpr = throwStmt->getSubExpr();
918924

919925
createConjunction(
@@ -1300,6 +1306,18 @@ class SyntacticElementConstraintGenerator
13001306
return {funcRef->getBodyResultType(), CTP_ReturnStmt};
13011307
}
13021308

1309+
Type getContextualThrownErrorType() const {
1310+
auto funcRef = AnyFunctionRef::fromDeclContext(context.getAsDeclContext());
1311+
if (!funcRef)
1312+
return Type();
1313+
1314+
if (auto *closure =
1315+
getAsExpr<ClosureExpr>(funcRef->getAbstractClosureExpr()))
1316+
return cs.getClosureType(closure)->getThrownError();
1317+
1318+
return funcRef->getThrownErrorType();
1319+
}
1320+
13031321
#define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \
13041322
llvm_unreachable("Unsupported statement kind " #STMT); \
13051323
}
@@ -2520,6 +2538,11 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution(
25202538
if (llvm::is_contained(solution.preconcurrencyClosures, closure))
25212539
closure->setIsolatedByPreconcurrency();
25222540

2541+
// Coerce the thrown type, if it was written explicitly.
2542+
if (closure->getExplicitThrownType()) {
2543+
closure->setExplicitThrownType(closureFnType->getThrownError());
2544+
}
2545+
25232546
// Coerce the result type, if it was written explicitly.
25242547
if (closure->hasExplicitResultType()) {
25252548
closure->setExplicitResultType(closureFnType->getResult());

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
4545
case ConstraintLocator::ApplyFunction:
4646
case ConstraintLocator::SequenceElementType:
4747
case ConstraintLocator::ClosureResult:
48+
case ConstraintLocator::ClosureThrownError:
4849
case ConstraintLocator::ClosureBody:
4950
case ConstraintLocator::ConstructorMember:
5051
case ConstraintLocator::ConstructorMemberType:
@@ -187,6 +188,10 @@ void LocatorPathElt::dump(raw_ostream &out) const {
187188
out << "closure result";
188189
break;
189190

191+
case ConstraintLocator::ClosureThrownError:
192+
out << "closure thrown error";
193+
break;
194+
190195
case ConstraintLocator::ClosureBody:
191196
out << "type of a closure body";
192197
break;

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5811,6 +5811,16 @@ void constraints::simplifyLocator(ASTNode &anchor,
58115811
}
58125812
break;
58135813

5814+
case ConstraintLocator::ClosureThrownError:
5815+
if (auto CE = getAsExpr<ClosureExpr>(anchor)) {
5816+
if (auto thrownTypeRepr = CE->getExplicitThrownTypeRepr()) {
5817+
anchor = thrownTypeRepr;
5818+
path = path.slice(1);
5819+
break;
5820+
}
5821+
}
5822+
break;
5823+
58145824
case ConstraintLocator::CoercionOperand: {
58155825
auto *CE = castToExpr<CoerceExpr>(anchor);
58165826
anchor = CE->getSubExpr()->getValueProvidingExpr();

lib/Sema/TypeCheckStmt.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,8 +1195,6 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
11951195
// Coerce the operand to the exception type.
11961196
auto E = TS->getSubExpr();
11971197

1198-
1199-
12001198
Type errorType;
12011199
if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) {
12021200
errorType = TheFunc->getThrownErrorType();

test/expr/closure/typed_throws.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows
2+
3+
enum MyError: Error {
4+
case fail
5+
}
6+
7+
enum MyBadError {
8+
case fail
9+
}
10+
11+
func testClosures() {
12+
let c1 = { () throws(MyError) in
13+
throw .fail
14+
}
15+
16+
let _: () -> Void = c1
17+
// expected-error@-1{{invalid conversion from throwing function of type '() throws(MyError) -> ()'}}
18+
19+
let _: () throws(MyError) -> Void = {
20+
throw .fail
21+
}
22+
23+
// FIXME: Terrible diagnostic.
24+
// expected-error@+1{{unable to infer closure type without a type annotation}}
25+
let _ = { () throws(MyBadError) in
26+
throw MyBadError.fail
27+
}
28+
29+
// We do not infer thrown error types from the body, because doing so would
30+
// break existing code.
31+
let c2 = { throw MyError.fail }
32+
let _: Int = c2
33+
// expected-error@-1{{cannot convert value of type '() throws -> ()'}}
34+
}
35+

0 commit comments

Comments
 (0)