diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 84fc3ea710ee..8b5f65a741a0 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -29,6 +29,7 @@ #define SWIFT_AST_AST_SCOPE_H #include "swift/AST/ASTNode.h" +#include "swift/AST/CatchNode.h" #include "swift/AST/NameLookup.h" #include "swift/AST/SimpleRequest.h" #include "swift/Basic/Compiler.h" @@ -85,6 +86,7 @@ class SILGenFunction; namespace ast_scope { class ASTScopeImpl; +class BraceStmtScope; class GenericTypeOrExtensionScope; class IterableTypeScope; class TypeAliasScope; @@ -211,6 +213,7 @@ class ASTScopeImpl : public ASTAllocated { #pragma mark common queries public: virtual NullablePtr getClosureIfClosureScope() const; + virtual NullablePtr getAsBraceStmtScope() const; virtual ASTContext &getASTContext() const; virtual NullablePtr getDeclIfAny() const { return nullptr; }; virtual NullablePtr getStmtIfAny() const { return nullptr; }; @@ -287,10 +290,18 @@ class ASTScopeImpl : public ASTAllocated { SourceFile *sourceFile, SourceLoc loc, llvm::function_ref consume); + static CatchNode lookupCatchNode(ModuleDecl *module, SourceLoc loc); + /// Scopes that cannot bind variables may set this to true to create more /// compact scope tree in the debug info. virtual bool ignoreInDebugInfo() const { return false; } + /// If this scope node represents a potential catch node, return body the + /// AST node describing the catch (a function, closure, or do...catch) and + /// the node of it's "body", i.e., the brace statement from which errors + /// thrown will be caught by that node. + virtual std::pair getCatchNodeBody() const; + #pragma mark - - lookup- starting point private: static const ASTScopeImpl *findStartingScopeForLookup(SourceFile *, @@ -824,6 +835,8 @@ class FunctionBodyScope : public ASTScopeImpl { Decl *getDecl() const { return decl; } bool ignoreInDebugInfo() const override { return true; } + std::pair getCatchNodeBody() const override; + protected: bool lookupLocalsOrMembers(DeclConsumer) const override; @@ -1069,6 +1082,8 @@ class ClosureParametersScope final : public ASTScopeImpl { NullablePtr getClosureIfClosureScope() const override { return closureExpr; } + std::pair getCatchNodeBody() const override; + NullablePtr getExprIfAny() const override { return closureExpr; } Expr *getExpr() const { return closureExpr; } bool ignoreInDebugInfo() const override { return true; } @@ -1440,6 +1455,8 @@ class DoCatchStmtScope final : public AbstractStmtScope { void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); public: + std::pair getCatchNodeBody() const override; + std::string getClassName() const override; Stmt *getStmt() const override { return stmt; } }; @@ -1648,6 +1665,8 @@ class BraceStmtScope final : public AbstractStmtScope { NullablePtr parentClosureIfAny() const; // public?? Stmt *getStmt() const override { return stmt; } + NullablePtr getAsBraceStmtScope() const override; + protected: bool lookupLocalsOrMembers(DeclConsumer) const override; }; diff --git a/include/swift/AST/CatchNode.h b/include/swift/AST/CatchNode.h new file mode 100644 index 000000000000..6b74b2814427 --- /dev/null +++ b/include/swift/AST/CatchNode.h @@ -0,0 +1,43 @@ +//===--- CatchNode.h - An AST node that catches errors -----------*- C++-*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_CATCHNODE_H +#define SWIFT_AST_CATCHNODE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Stmt.h" + +namespace swift { + +/// An AST node that represents a point where a thrown error can be caught and +/// or rethrown, which includes functions do...catch statements. +class CatchNode: public llvm::PointerUnion< + AbstractFunctionDecl *, AbstractClosureExpr *, DoCatchStmt * + > { +public: + using PointerUnion::PointerUnion; + + /// Determine the thrown error type within the region of this catch node + /// where it will catch (and possibly rethrow) errors. All of the errors + /// thrown from within that region will be converted to this error type. + /// + /// Returns the thrown error type for a throwing context, or \c llvm::None + /// if this is a non-throwing context. + llvm::Optional getThrownErrorTypeInContext(ASTContext &ctx) const; +}; + +} // end namespace swift + +#endif // SWIFT_AST_CATCHNODE_H diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index d4d78b453848..45f38444e686 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -18,6 +18,7 @@ #define SWIFT_AST_NAME_LOOKUP_H #include "swift/AST/ASTVisitor.h" +#include "swift/AST/CatchNode.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/Module.h" @@ -833,6 +834,25 @@ class ASTScope : public ASTAllocated { SourceFile *sourceFile, SourceLoc loc, llvm::function_ref consume); + /// Look up the scope tree for the nearest point at which an error thrown from + /// this location can be caught or rethrown. + /// + /// For example, given this code: + /// + /// \code + /// func f() throws { + /// do { + /// try g() // A + /// } catch { + /// throw ErrorWrapper(error) // B + /// } + /// } + /// \endcode + /// + /// At the point marked A, the catch node is the enclosing do...catch + /// statement. At the point marked B, the catch node is the function itself. + static CatchNode lookupCatchNode(ModuleDecl *module, SourceLoc loc); + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &) const; void dumpOneScopeMapLocation(std::pair); diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index aa80e71159bf..8cb4c0c7f001 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -66,6 +66,10 @@ void ASTScope::lookupEnclosingMacroScope( return ASTScopeImpl::lookupEnclosingMacroScope(sourceFile, loc, body); } +CatchNode ASTScope::lookupCatchNode(ModuleDecl *module, SourceLoc loc) { + return ASTScopeImpl::lookupCatchNode(module, loc); +} + #if SWIFT_COMPILER_IS_MSVC #pragma warning(push) #pragma warning(disable : 4996) @@ -97,10 +101,65 @@ NullablePtr BraceStmtScope::parentClosureIfAny() const { return !getParent() ? nullptr : getParent().get()->getClosureIfClosureScope(); } +NullablePtr BraceStmtScope::getAsBraceStmtScope() const { + return this; +} + NullablePtr ASTScopeImpl::getClosureIfClosureScope() const { return nullptr; } +NullablePtr ASTScopeImpl::getAsBraceStmtScope() const { + return nullptr; +} + +std::pair +ASTScopeImpl::getCatchNodeBody() const { + return { nullptr, nullptr }; +} + +std::pair +ClosureParametersScope::getCatchNodeBody() const { + const BraceStmtScope *body = nullptr; + const auto &children = getChildren(); + if (!children.empty()) { + body = children[0]->getAsBraceStmtScope().getPtrOrNull(); + assert(body && "Not a brace statement?"); + } + return { const_cast(closureExpr), body }; +} + +std::pair +FunctionBodyScope::getCatchNodeBody() const { + const BraceStmtScope *body = nullptr; + const auto &children = getChildren(); + if (!children.empty()) { + body = children[0]->getAsBraceStmtScope().getPtrOrNull(); + assert(body && "Not a brace statement?"); + } + return { const_cast(decl), body }; +} + +/// Determine whether this is an empty brace statement, which doesn't have a +/// node associated with it. +static bool isEmptyBraceStmt(Stmt *stmt) { + if (auto braceStmt = dyn_cast_or_null(stmt)) + return braceStmt->empty(); + + return false; +} + +std::pair +DoCatchStmtScope::getCatchNodeBody() const { + const BraceStmtScope *body = nullptr; + const auto &children = getChildren(); + if (!children.empty() && !isEmptyBraceStmt(stmt->getBody())) { + body = children[0]->getAsBraceStmtScope().getPtrOrNull(); + assert(body && "Not a brace statement?"); + } + return { const_cast(stmt), body }; +} + SourceManager &ASTScopeImpl::getSourceManager() const { return getASTContext().SourceMgr; } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index fd7ed7192542..6493b68305e4 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -712,3 +712,30 @@ void ASTScopeImpl::lookupEnclosingMacroScope( } while ((scope = scope->getParent().getPtrOrNull())); } + +CatchNode ASTScopeImpl::lookupCatchNode(ModuleDecl *module, SourceLoc loc) { + auto sourceFile = module->getSourceFileContainingLocation(loc); + if (!sourceFile) + return nullptr; + + auto *fileScope = sourceFile->getScope().impl; + const auto *innermost = fileScope->findInnermostEnclosingScope( + module, loc, nullptr); + ASTScopeAssert(innermost->getWasExpanded(), + "If looking in a scope, it must have been expanded."); + + // Look for a body scope that's the + const BraceStmtScope *innerBodyScope = nullptr; + for (auto scope = innermost; scope; scope = scope->getParent().getPtrOrNull()) { + // If we are at a catch node and in the body of the region from which that + // node catches thrown errors, we have our result. + auto caught = scope->getCatchNodeBody(); + if (caught.first && caught.second == innerBodyScope) { + return caught.first; + } + + innerBodyScope = scope->getAsBraceStmtScope().getPtrOrNull(); + } + + return nullptr; +} diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e5a5370c67e4..2fd424aca859 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -957,14 +957,24 @@ Type AbstractFunctionDecl::getThrownInterfaceType() const { llvm::Optional AbstractFunctionDecl::getEffectiveThrownErrorType() const { + // FIXME: Only getters can have thrown error types right now, and DidSet + // has a cyclic reference if we try to get its interface type here. Find a + // better way to express this. + if (auto accessor = dyn_cast(this)) { + if (accessor->getAccessorKind() != AccessorKind::Get) + return llvm::None; + } + Type interfaceType = getInterfaceType(); if (hasImplicitSelfDecl()) { if (auto fnType = interfaceType->getAs()) interfaceType = fnType->getResult(); } - return interfaceType->castTo() - ->getEffectiveThrownErrorType(); + if (auto fnType = interfaceType->getAs()) + return fnType->getEffectiveThrownErrorType(); + + return llvm::None; } Expr *AbstractFunctionDecl::getSingleExpressionBody() const { @@ -11398,3 +11408,32 @@ MacroDiscriminatorContext::getParentOf(FreestandingMacroExpansion *expansion) { return getParentOf( expansion->getPoundLoc(), expansion->getDeclContext()); } + +llvm::Optional +CatchNode::getThrownErrorTypeInContext(ASTContext &ctx) const { + if (auto func = dyn_cast()) { + if (auto thrownError = func->getEffectiveThrownErrorType()) + return func->mapTypeIntoContext(*thrownError); + + return llvm::None; + } + + if (auto closure = dyn_cast()) { + if (closure->getType()) + return closure->getEffectiveThrownType(); + + // FIXME: Should we lazily compute this? + return llvm::None; + } + + auto doCatch = get(); + if (auto thrownError = doCatch->getCaughtErrorType()) { + if (thrownError->isNever()) + return llvm::None; + + return thrownError; + } + + // If we haven't computed the error type yet, do so now. + return ctx.getErrorExistentialType(); +} diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 6f075b541ba7..939800af44c4 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -599,7 +599,7 @@ struct InferRequirementsWalker : public TypeWalker { DifferentiabilityKind::Linear); } - // Infer that the thrown error type conforms to Error. + // Infer that the thrown error type of a function type conforms to Error. if (auto thrownError = fnTy->getThrownError()) { if (auto errorProtocol = ctx.getErrorDecl()) { addConformanceConstraint(thrownError, errorProtocol); diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 08978a78e5f0..a61483fb8d66 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -473,12 +473,15 @@ bool DoCatchStmt::isSyntacticallyExhaustive() const { } Type DoCatchStmt::getCaughtErrorType() const { - return getCatches() + auto firstPattern = getCatches() .front() ->getCaseLabelItems() .front() - .getPattern() - ->getType(); + .getPattern(); + if (firstPattern->hasType()) + return firstPattern->getType(); + + return Type(); } void LabeledConditionalStmt::setCond(StmtCondition e) { diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 2d6487152a65..abb61117e486 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -918,12 +918,15 @@ class ApplyClassifier { Classification classifyConformance(ProtocolConformanceRef conformanceRef, EffectKind kind) { + if (conformanceRef.isInvalid()) + return Classification::forInvalidCode(); + if (conformanceRef.hasEffect(kind)) { assert(kind == EffectKind::Throws); // there is no async ASTContext &ctx = conformanceRef.getRequirement()->getASTContext(); // FIXME: typed throws, if it becomes a thing for conformances return Classification::forThrows( - ctx.getAnyExistentialType(), + ctx.getErrorExistentialType(), ConditionalEffectKind::Conditional, PotentialEffectReason::forConformance()); } @@ -1718,14 +1721,15 @@ class Context { private: static Context getContextForPatternBinding(PatternBindingDecl *pbd) { if (!pbd->isStatic() && pbd->getDeclContext()->isTypeContext()) { - return Context(Kind::IVarInitializer); + return Context(Kind::IVarInitializer, pbd->getDeclContext()); } else { - return Context(Kind::GlobalVarInitializer); + return Context(Kind::GlobalVarInitializer, pbd->getDeclContext()); } } Kind TheKind; llvm::Optional Function; + DeclContext *DC; bool HandlesErrors = false; bool HandlesAsync = false; @@ -1736,14 +1740,15 @@ class Context { bool DiagnoseErrorOnTry = false; InterpolatedStringLiteralExpr *InterpolatedString = nullptr; - explicit Context(Kind kind) - : TheKind(kind), Function(llvm::None), HandlesErrors(false) { + explicit Context(Kind kind, DeclContext *dc) + : TheKind(kind), Function(llvm::None), DC(dc), HandlesErrors(false) { assert(TheKind != Kind::PotentiallyHandled); } explicit Context(bool handlesErrors, bool handlesAsync, - llvm::Optional function) - : TheKind(Kind::PotentiallyHandled), Function(function), + llvm::Optional function, + DeclContext *dc) + : TheKind(Kind::PotentiallyHandled), Function(function), DC(dc), HandlesErrors(handlesErrors), HandlesAsync(handlesAsync) {} public: @@ -1820,7 +1825,7 @@ class Context { static Context forTopLevelCode(TopLevelCodeDecl *D) { // Top-level code implicitly handles errors. return Context(/*handlesErrors=*/true, - /*handlesAsync=*/D->isAsyncContext(), llvm::None); + /*handlesAsync=*/D->isAsyncContext(), llvm::None, D); } static Context forFunction(AbstractFunctionDecl *D) { @@ -1840,20 +1845,20 @@ class Context { } } - return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D)); + return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D), D); } - static Context forDeferBody() { - return Context(Kind::DeferBody); + static Context forDeferBody(DeclContext *dc) { + return Context(Kind::DeferBody, dc); } static Context forInitializer(Initializer *init) { if (isa(init)) { - return Context(Kind::DefaultArgument); + return Context(Kind::DefaultArgument, init); } if (isa(init)) { - return Context(Kind::PropertyWrapper); + return Context(Kind::PropertyWrapper, init); } auto *binding = cast(init)->getBinding(); @@ -1863,7 +1868,7 @@ class Context { } static Context forEnumElementInitializer(EnumElementDecl *elt) { - return Context(Kind::EnumElementInitializer); + return Context(Kind::EnumElementInitializer, elt); } static Context forClosure(AbstractClosureExpr *E) { @@ -1877,15 +1882,15 @@ class Context { } } - return Context(closureTypeThrows, closureTypeIsAsync, AnyFunctionRef(E)); + return Context(closureTypeThrows, closureTypeIsAsync, AnyFunctionRef(E), E); } - static Context forCatchPattern(CaseStmt *S) { - return Context(Kind::CatchPattern); + static Context forCatchPattern(CaseStmt *S, DeclContext *dc) { + return Context(Kind::CatchPattern, dc); } - static Context forCatchGuard(CaseStmt *S) { - return Context(Kind::CatchGuard); + static Context forCatchGuard(CaseStmt *S, DeclContext *dc) { + return Context(Kind::CatchGuard, dc); } static Context forPatternBinding(PatternBindingDecl *binding) { @@ -1909,6 +1914,8 @@ class Context { Kind getKind() const { return TheKind; } + DeclContext *getDeclContext() const { return DC; } + bool handlesThrows(ConditionalEffectKind errorKind) const { switch (errorKind) { case ConditionalEffectKind::None: @@ -2587,6 +2594,19 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } }; + /// Retrieve the type of the error that can be caught when an error is + /// thrown from the given location. + Type getCaughtErrorTypeAt(SourceLoc loc) { + auto module = CurContext.getDeclContext()->getParentModule(); + if (CatchNode catchNode = ASTScope::lookupCatchNode(module, loc)) { + if (auto caughtType = catchNode.getThrownErrorTypeInContext(Ctx)) + return *caughtType; + } + + // Fall back to the error existential. + return Ctx.getErrorExistentialType(); + } + public: CheckEffectsCoverage(ASTContext &ctx, Context initialContext) : Ctx(ctx), CurContext(initialContext), @@ -2706,6 +2726,11 @@ class CheckEffectsCoverage : public EffectsHandlingWalker // specialized diagnostic about non-exhaustive catches. if (!CurContext.handlesThrows(ConditionalEffectKind::Conditional)) { CurContext.setNonExhaustiveCatch(true); + } else if (Type rethrownErrorType = S->getCaughtErrorType()) { + // We're implicitly rethrowing the error out of this do..catch, so make + // sure that we can throw an error of this type out of this context. + auto catches = S->getCatches(); + checkThrownErrorType(catches.back()->getEndLoc(), rethrownErrorType); } S->getBody()->walk(*this); @@ -2727,14 +2752,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } void checkCatch(CaseStmt *S, ConditionalEffectKind doThrowingKind) { + auto dc = CurContext.getDeclContext(); for (auto &LabelItem : S->getMutableCaseLabelItems()) { // The pattern and guard aren't allowed to throw. { - ContextScope scope(*this, Context::forCatchPattern(S)); + ContextScope scope(*this, Context::forCatchPattern(S, dc)); LabelItem.getPattern()->walk(*this); } if (auto guard = LabelItem.getGuardExpr()) { - ContextScope scope(*this, Context::forCatchGuard(S)); + ContextScope scope(*this, Context::forCatchGuard(S, dc)); guard->walk(*this); } } @@ -2943,11 +2969,27 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } else if (!isTryCovered) { CurContext.diagnoseUncoveredThrowSite(Ctx, E, // we want this one to trigger classification.getThrowReason()); + } else { + checkThrownErrorType(E.getStartLoc(), classification.getThrownError()); } break; } } + /// Check the thrown error type against the type that can be caught or + /// rethrown by the context. + void checkThrownErrorType(SourceLoc loc, Type thrownErrorType) { + Type caughtErrorType = getCaughtErrorTypeAt(loc); + if (caughtErrorType->isEqual(thrownErrorType)) + return; + + OpaqueValueExpr *opaque = new (Ctx) OpaqueValueExpr(loc, thrownErrorType); + Expr *rethrowExpr = opaque; + TypeChecker::typeCheckExpression( + rethrowExpr, CurContext.getDeclContext(), + {caughtErrorType, /*FIXME:*/CTP_ThrowStmt}); + } + ShouldRecurse_t checkAwait(AwaitExpr *E) { // Walk the operand. @@ -3206,7 +3248,7 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) { auto isDeferBody = isa(fn) && cast(fn)->isDeferBody(); auto context = - isDeferBody ? Context::forDeferBody() : Context::forFunction(fn); + isDeferBody ? Context::forDeferBody(fn) : Context::forFunction(fn); auto &ctx = fn->getASTContext(); CheckEffectsCoverage checker(ctx, context); diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 3ab084b813c6..f4b61a6ec8bf 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -758,6 +758,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, : subscr ? subscr->getEffectfulGetAccessor() : nullptr; if (effectiveFunc) { + // Infer constraints from the thrown type of a declaration. if (auto thrownTypeRepr = effectiveFunc->getThrownTypeRepr()) { auto thrownOptions = baseOptions | TypeResolutionFlags::Direct; const auto thrownType = resolution.withOptions(thrownOptions) @@ -767,10 +768,12 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, inferenceSources.emplace_back(thrownTypeRepr, thrownType); // Add conformance of this type to the Error protocol. - if (auto errorProtocol = ctx.getErrorDecl()) { - extraReqs.push_back( - Requirement(RequirementKind::Conformance, thrownType, - errorProtocol->getDeclaredInterfaceType())); + if (thrownType->isTypeParameter()) { + if (auto errorProtocol = ctx.getErrorDecl()) { + extraReqs.push_back( + Requirement(RequirementKind::Conformance, thrownType, + errorProtocol->getDeclaredInterfaceType())); + } } } } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index e4f8c1aae1df..3d76eb01a5af 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1195,11 +1195,16 @@ class StmtChecker : public StmtVisitor { // Coerce the operand to the exception type. auto E = TS->getSubExpr(); + // Look up the catch node for this "throw" to determine the error type. + CatchNode catchNode = ASTScope::lookupCatchNode( + DC->getParentModule(), TS->getThrowLoc()); Type errorType; - if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) { - errorType = TheFunc->getThrownErrorType(); + if (catchNode) { + errorType = catchNode.getThrownErrorTypeInContext(getASTContext()) + .value_or(Type()); } + // If there was no error type, use 'any Error'. We'll check it later. if (!errorType) { errorType = getASTContext().getErrorExistentialType(); } @@ -1684,21 +1689,6 @@ class StmtChecker : public StmtVisitor { CaseParentKind::DoCatch, limitExhaustivityChecks, caughtErrorType); - if (!S->isSyntacticallyExhaustive()) { - // If we're implicitly rethrowing the error out of this do..catch, make - // sure that we can throw an error of this type out of this context. - // FIXME: Unify this lookup of the type with that from ThrowStmt. - if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) { - if (Type expectedErrorType = TheFunc->getThrownErrorType()) { - OpaqueValueExpr *opaque = new (Ctx) OpaqueValueExpr( - catches.back()->getEndLoc(), caughtErrorType); - Expr *rethrowExpr = opaque; - TypeChecker::typeCheckExpression( - rethrowExpr, DC, {expectedErrorType, CTP_ThrowStmt}); - } - } - } - return S; } diff --git a/test/Concurrency/typed_throws.swift b/test/Concurrency/typed_throws.swift new file mode 100644 index 000000000000..a570e654ccde --- /dev/null +++ b/test/Concurrency/typed_throws.swift @@ -0,0 +1,16 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows + +// REQUIRES: concurrency + +enum MyError: Error { + case failed + case epicFailed +} + + +@available(SwiftStdlib 5.1, *) +func testAsyncFor(seq: S) async throws(MyError) { + // expected-error@+1{{thrown expression type 'any Error' cannot be converted to error type 'MyError'}} + for try await _ in seq { + } +} diff --git a/test/stmt/typed_throws.swift b/test/stmt/typed_throws.swift index 7696dee1c142..cf9c48f2fa8d 100644 --- a/test/stmt/typed_throws.swift +++ b/test/stmt/typed_throws.swift @@ -74,10 +74,10 @@ func testDoCatchMultiErrorType() { try doSomething() try doHomework() } catch .failed { // expected-error{{type 'any Error' has no member 'failed'}} - + } catch { let _: Int = error // expected-error{{cannot convert value of type 'any Error' to specified type 'Int'}} - } + } } func testDoCatchRethrowsUntyped() throws { @@ -96,7 +96,7 @@ func testDoCatchRethrowsTyped() throws(HomeworkError) { do { try doSomething() } catch .failed { - + } // expected-error{{thrown expression type 'MyError' cannot be converted to error type 'HomeworkError'}} do { @@ -114,8 +114,20 @@ func testDoCatchRethrowsTyped() throws(HomeworkError) { } // okay, the thrown 'any Error' has been caught } -func testTryIncompatibleTyped() throws(HomeworkError) { +func testTryIncompatibleTyped(cond: Bool) throws(HomeworkError) { try doHomework() // okay - try doSomething() // FIXME: should error + try doSomething() // expected-error{{thrown expression type 'MyError' cannot be converted to error type 'HomeworkError'}} + + do { + if cond { + throw .dogAteIt // expected-error{{type 'any Error' has no member 'dogAteIt'}} + } else { + try doSomething() + } + } catch let error as Never { + // expected-warning@-1{{'catch' block is unreachable because no errors are thrown in 'do' block}} + // expected-warning@-2{{'as' test is always true}} + throw .forgot + } }