Skip to content

Commit 7f82b2a

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 54589e1 commit 7f82b2a

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
@@ -2453,6 +2453,49 @@ namespace {
24532453
auto resultLocator =
24542454
CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult);
24552455

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

5812+
case ConstraintLocator::ClosureThrownError:
5813+
if (auto CE = getAsExpr<ClosureExpr>(anchor)) {
5814+
if (auto thrownTypeRepr = CE->getExplicitThrownTypeRepr()) {
5815+
anchor = thrownTypeRepr;
5816+
path = path.slice(1);
5817+
break;
5818+
}
5819+
}
5820+
break;
5821+
58125822
case ConstraintLocator::CoercionOperand: {
58135823
auto *CE = castToExpr<CoerceExpr>(anchor);
58145824
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)