Skip to content

Commit 99e327d

Browse files
committed
[Typed throws] Check thrown error types of for applications/subscripts/etc.
Whenever there is a throwing operation such as a call, subscript, or property access, check that the error type thrown from that operation can be caught/rethrown from the current context.
1 parent 3dd4df2 commit 99e327d

File tree

3 files changed

+61
-37
lines changed

3 files changed

+61
-37
lines changed

lib/Sema/TypeCheckEffects.cpp

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,14 +1718,15 @@ class Context {
17181718
private:
17191719
static Context getContextForPatternBinding(PatternBindingDecl *pbd) {
17201720
if (!pbd->isStatic() && pbd->getDeclContext()->isTypeContext()) {
1721-
return Context(Kind::IVarInitializer);
1721+
return Context(Kind::IVarInitializer, pbd->getDeclContext());
17221722
} else {
1723-
return Context(Kind::GlobalVarInitializer);
1723+
return Context(Kind::GlobalVarInitializer, pbd->getDeclContext());
17241724
}
17251725
}
17261726

17271727
Kind TheKind;
17281728
llvm::Optional<AnyFunctionRef> Function;
1729+
DeclContext *DC;
17291730
bool HandlesErrors = false;
17301731
bool HandlesAsync = false;
17311732

@@ -1736,14 +1737,15 @@ class Context {
17361737
bool DiagnoseErrorOnTry = false;
17371738
InterpolatedStringLiteralExpr *InterpolatedString = nullptr;
17381739

1739-
explicit Context(Kind kind)
1740-
: TheKind(kind), Function(llvm::None), HandlesErrors(false) {
1740+
explicit Context(Kind kind, DeclContext *dc)
1741+
: TheKind(kind), Function(llvm::None), DC(dc), HandlesErrors(false) {
17411742
assert(TheKind != Kind::PotentiallyHandled);
17421743
}
17431744

17441745
explicit Context(bool handlesErrors, bool handlesAsync,
1745-
llvm::Optional<AnyFunctionRef> function)
1746-
: TheKind(Kind::PotentiallyHandled), Function(function),
1746+
llvm::Optional<AnyFunctionRef> function,
1747+
DeclContext *dc)
1748+
: TheKind(Kind::PotentiallyHandled), Function(function), DC(dc),
17471749
HandlesErrors(handlesErrors), HandlesAsync(handlesAsync) {}
17481750

17491751
public:
@@ -1820,7 +1822,7 @@ class Context {
18201822
static Context forTopLevelCode(TopLevelCodeDecl *D) {
18211823
// Top-level code implicitly handles errors.
18221824
return Context(/*handlesErrors=*/true,
1823-
/*handlesAsync=*/D->isAsyncContext(), llvm::None);
1825+
/*handlesAsync=*/D->isAsyncContext(), llvm::None, D);
18241826
}
18251827

18261828
static Context forFunction(AbstractFunctionDecl *D) {
@@ -1840,20 +1842,20 @@ class Context {
18401842
}
18411843
}
18421844

1843-
return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D));
1845+
return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D), D);
18441846
}
18451847

1846-
static Context forDeferBody() {
1847-
return Context(Kind::DeferBody);
1848+
static Context forDeferBody(DeclContext *dc) {
1849+
return Context(Kind::DeferBody, dc);
18481850
}
18491851

18501852
static Context forInitializer(Initializer *init) {
18511853
if (isa<DefaultArgumentInitializer>(init)) {
1852-
return Context(Kind::DefaultArgument);
1854+
return Context(Kind::DefaultArgument, init);
18531855
}
18541856

18551857
if (isa<PropertyWrapperInitializer>(init)) {
1856-
return Context(Kind::PropertyWrapper);
1858+
return Context(Kind::PropertyWrapper, init);
18571859
}
18581860

18591861
auto *binding = cast<PatternBindingInitializer>(init)->getBinding();
@@ -1863,7 +1865,7 @@ class Context {
18631865
}
18641866

18651867
static Context forEnumElementInitializer(EnumElementDecl *elt) {
1866-
return Context(Kind::EnumElementInitializer);
1868+
return Context(Kind::EnumElementInitializer, elt);
18671869
}
18681870

18691871
static Context forClosure(AbstractClosureExpr *E) {
@@ -1877,15 +1879,15 @@ class Context {
18771879
}
18781880
}
18791881

1880-
return Context(closureTypeThrows, closureTypeIsAsync, AnyFunctionRef(E));
1882+
return Context(closureTypeThrows, closureTypeIsAsync, AnyFunctionRef(E), E);
18811883
}
18821884

1883-
static Context forCatchPattern(CaseStmt *S) {
1884-
return Context(Kind::CatchPattern);
1885+
static Context forCatchPattern(CaseStmt *S, DeclContext *dc) {
1886+
return Context(Kind::CatchPattern, dc);
18851887
}
18861888

1887-
static Context forCatchGuard(CaseStmt *S) {
1888-
return Context(Kind::CatchGuard);
1889+
static Context forCatchGuard(CaseStmt *S, DeclContext *dc) {
1890+
return Context(Kind::CatchGuard, dc);
18891891
}
18901892

18911893
static Context forPatternBinding(PatternBindingDecl *binding) {
@@ -1909,6 +1911,8 @@ class Context {
19091911

19101912
Kind getKind() const { return TheKind; }
19111913

1914+
DeclContext *getDeclContext() const { return DC; }
1915+
19121916
bool handlesThrows(ConditionalEffectKind errorKind) const {
19131917
switch (errorKind) {
19141918
case ConditionalEffectKind::None:
@@ -2587,6 +2591,19 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25872591
}
25882592
};
25892593

2594+
/// Retrieve the type of the error that can be caught when an error is
2595+
/// thrown from the given location.
2596+
Type getCaughtErrorTypeAt(SourceLoc loc) {
2597+
auto module = CurContext.getDeclContext()->getParentModule();
2598+
if (CatchNode catchNode = ASTScope::lookupCatchNode(module, loc)) {
2599+
if (auto caughtType = catchNode.getThrownErrorTypeInContext(Ctx))
2600+
return *caughtType;
2601+
}
2602+
2603+
// Fall back to the error existential.
2604+
return Ctx.getErrorExistentialType();
2605+
}
2606+
25902607
public:
25912608
CheckEffectsCoverage(ASTContext &ctx, Context initialContext)
25922609
: Ctx(ctx), CurContext(initialContext),
@@ -2706,6 +2723,11 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27062723
// specialized diagnostic about non-exhaustive catches.
27072724
if (!CurContext.handlesThrows(ConditionalEffectKind::Conditional)) {
27082725
CurContext.setNonExhaustiveCatch(true);
2726+
} else if (Type rethrownErrorType = S->getCaughtErrorType()) {
2727+
// We're implicitly rethrowing the error out of this do..catch, so make
2728+
// sure that we can throw an error of this type out of this context.
2729+
auto catches = S->getCatches();
2730+
checkThrownErrorType(catches.back()->getEndLoc(), rethrownErrorType);
27092731
}
27102732

27112733
S->getBody()->walk(*this);
@@ -2727,14 +2749,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27272749
}
27282750

27292751
void checkCatch(CaseStmt *S, ConditionalEffectKind doThrowingKind) {
2752+
auto dc = CurContext.getDeclContext();
27302753
for (auto &LabelItem : S->getMutableCaseLabelItems()) {
27312754
// The pattern and guard aren't allowed to throw.
27322755
{
2733-
ContextScope scope(*this, Context::forCatchPattern(S));
2756+
ContextScope scope(*this, Context::forCatchPattern(S, dc));
27342757
LabelItem.getPattern()->walk(*this);
27352758
}
27362759
if (auto guard = LabelItem.getGuardExpr()) {
2737-
ContextScope scope(*this, Context::forCatchGuard(S));
2760+
ContextScope scope(*this, Context::forCatchGuard(S, dc));
27382761
guard->walk(*this);
27392762
}
27402763
}
@@ -2943,11 +2966,27 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
29432966
} else if (!isTryCovered) {
29442967
CurContext.diagnoseUncoveredThrowSite(Ctx, E, // we want this one to trigger
29452968
classification.getThrowReason());
2969+
} else {
2970+
checkThrownErrorType(E.getStartLoc(), classification.getThrownError());
29462971
}
29472972
break;
29482973
}
29492974
}
29502975

2976+
/// Check the thrown error type against the type that can be caught or
2977+
/// rethrown by the context.
2978+
void checkThrownErrorType(SourceLoc loc, Type thrownErrorType) {
2979+
Type caughtErrorType = getCaughtErrorTypeAt(loc);
2980+
if (caughtErrorType->isEqual(thrownErrorType))
2981+
return;
2982+
2983+
OpaqueValueExpr *opaque = new (Ctx) OpaqueValueExpr(loc, thrownErrorType);
2984+
Expr *rethrowExpr = opaque;
2985+
TypeChecker::typeCheckExpression(
2986+
rethrowExpr, CurContext.getDeclContext(),
2987+
{caughtErrorType, /*FIXME:*/CTP_ThrowStmt});
2988+
}
2989+
29512990
ShouldRecurse_t checkAwait(AwaitExpr *E) {
29522991

29532992
// Walk the operand.
@@ -3206,7 +3245,7 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) {
32063245

32073246
auto isDeferBody = isa<FuncDecl>(fn) && cast<FuncDecl>(fn)->isDeferBody();
32083247
auto context =
3209-
isDeferBody ? Context::forDeferBody() : Context::forFunction(fn);
3248+
isDeferBody ? Context::forDeferBody(fn) : Context::forFunction(fn);
32103249
auto &ctx = fn->getASTContext();
32113250
CheckEffectsCoverage checker(ctx, context);
32123251

lib/Sema/TypeCheckStmt.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,21 +1689,6 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
16891689
CaseParentKind::DoCatch, limitExhaustivityChecks,
16901690
caughtErrorType);
16911691

1692-
if (!S->isSyntacticallyExhaustive()) {
1693-
// If we're implicitly rethrowing the error out of this do..catch, make
1694-
// sure that we can throw an error of this type out of this context.
1695-
// FIXME: Unify this lookup of the type with that from ThrowStmt.
1696-
if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) {
1697-
if (Type expectedErrorType = TheFunc->getThrownErrorType()) {
1698-
OpaqueValueExpr *opaque = new (Ctx) OpaqueValueExpr(
1699-
catches.back()->getEndLoc(), caughtErrorType);
1700-
Expr *rethrowExpr = opaque;
1701-
TypeChecker::typeCheckExpression(
1702-
rethrowExpr, DC, {expectedErrorType, CTP_ThrowStmt});
1703-
}
1704-
}
1705-
}
1706-
17071692
return S;
17081693
}
17091694

test/stmt/typed_throws.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func testDoCatchRethrowsTyped() throws(HomeworkError) {
117117
func testTryIncompatibleTyped(cond: Bool) throws(HomeworkError) {
118118
try doHomework() // okay
119119

120-
try doSomething() // FIXME: should error
120+
try doSomething() // expected-error{{thrown expression type 'MyError' cannot be converted to error type 'HomeworkError'}}
121121

122122
do {
123123
if cond {

0 commit comments

Comments
 (0)