From ef642098f2ec0a8206676084943325da6d346f3f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Sep 2023 14:11:46 -0700 Subject: [PATCH 1/8] [Typed throws] Parsing and AST representation for typed errors Parse typed throw specifiers as `throws(X)` in every place where there are effects specified, and record the resulting thrown error type in the AST except the type system. This includes: * `FunctionTypeRepr`, for the parsed representation of types * `AbstractFunctionDecl`, for various function-like declarations * `ClosureExpr`, for closures * `ArrowExpr`, for parsing of types within expression context This also introduces some serialization logic for the thrown error type of function-like declarations, along with an API to extract the thrown interface type from one of those declarations, although right now it will either be `Error` or empty. --- include/swift/AST/CASTBridging.h | 9 ++- include/swift/AST/Decl.h | 36 +++++++++-- include/swift/AST/DiagnosticsParse.def | 7 +++ include/swift/AST/Expr.h | 41 +++++++++--- include/swift/AST/TypeRepr.h | 8 ++- include/swift/Parse/Parser.h | 11 +++- lib/AST/ASTDumper.cpp | 4 ++ lib/AST/ASTPrinter.cpp | 13 +++- lib/AST/ASTWalker.cpp | 15 +++++ lib/AST/Builtins.cpp | 6 +- lib/AST/CASTBridging.cpp | 15 +++-- lib/AST/Decl.cpp | 62 +++++++++++++------ lib/AST/Expr.cpp | 7 +++ lib/AST/TypeRepr.cpp | 7 +++ lib/ASTGen/Sources/ASTGen/Decls.swift | 2 + lib/ASTGen/Sources/ASTGen/Types.swift | 1 + lib/ClangImporter/ClangImporter.cpp | 13 ++-- lib/ClangImporter/ImportDecl.cpp | 18 +++--- lib/ClangImporter/SwiftDeclSynthesizer.cpp | 58 ++++++++++------- lib/Parse/ParseDecl.cpp | 27 +++++--- lib/Parse/ParseExpr.cpp | 47 ++++++++++---- lib/Parse/ParsePattern.cpp | 50 ++++++++++++--- lib/Parse/ParseStmt.cpp | 1 + lib/Parse/ParseType.cpp | 23 +++++-- lib/SILGen/SILGenFunction.cpp | 3 +- lib/SILGen/SILGenProlog.cpp | 4 +- lib/Sema/CodeSynthesis.cpp | 7 +++ lib/Sema/CodeSynthesisDistributedActor.cpp | 4 +- lib/Sema/DebuggerTestingTransform.cpp | 2 +- .../DerivedConformanceAdditiveArithmetic.cpp | 1 + lib/Sema/DerivedConformanceCodable.cpp | 6 +- lib/Sema/DerivedConformanceCodingKey.cpp | 1 + lib/Sema/DerivedConformanceComparable.cpp | 1 + lib/Sema/DerivedConformanceDifferentiable.cpp | 1 + .../DerivedConformanceDistributedActor.cpp | 6 +- .../DerivedConformanceEquatableHashable.cpp | 7 ++- .../DerivedConformanceRawRepresentable.cpp | 2 +- lib/Sema/DerivedConformances.cpp | 4 +- lib/Sema/PreCheckExpr.cpp | 9 ++- lib/Sema/TypeCheckAttr.cpp | 1 + lib/Sema/TypeCheckStorage.cpp | 11 ++-- lib/Serialization/Deserialization.cpp | 26 ++++++-- lib/Serialization/ModuleFormat.h | 5 +- lib/Serialization/Serialization.cpp | 3 + test/Parse/typed_throws.swift | 31 ++++++++++ unittests/Sema/ConstraintGenerationTests.cpp | 1 + .../Sema/ConstraintSimplificationTests.cpp | 1 + 47 files changed, 473 insertions(+), 145 deletions(-) create mode 100644 test/Parse/typed_throws.swift diff --git a/include/swift/AST/CASTBridging.h b/include/swift/AST/CASTBridging.h index 84bf3cf500a2..8d2256b91a74 100644 --- a/include/swift/AST/CASTBridging.h +++ b/include/swift/AST/CASTBridging.h @@ -341,24 +341,26 @@ void AbstractFunctionDecl_setBody(void *opaqueBody, void *opaqueDecl); SWIFT_NAME("FuncDecl_create(astContext:declContext:staticLoc:funcKeywordLoc:" "name:nameLoc:genericParamList:parameterList:asyncSpecifierLoc:" - "throwsSpecifierLoc:returnType:genericWhereClause:)") + "throwsSpecifierLoc:thrownType:returnType:genericWhereClause:)") struct BridgedDeclContextAndDecl FuncDecl_create(BridgedASTContext cContext, BridgedDeclContext cDeclContext, BridgedSourceLoc cStaticLoc, BridgedSourceLoc cFuncKeywordLoc, BridgedIdentifier cName, BridgedSourceLoc cNameLoc, void *_Nullable opaqueGenericParamList, void *opaqueParameterList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, void *_Nullable opaqueReturnType, + BridgedSourceLoc cThrowsLoc, void *_Nullable opaqueThrownType, + void *_Nullable opaqueReturnType, void *_Nullable opaqueGenericWhereClause); SWIFT_NAME("ConstructorDecl_create(astContext:declContext:initKeywordLoc:" "failabilityMarkLoc:isIUO:genericParamList:parameterList:" - "asyncSpecifierLoc:throwsSpecifierLoc:genericWhereClause:)") + "asyncSpecifierLoc:throwsSpecifierLoc:thrownType:genericWhereClause:)") BridgedDeclContextAndDecl ConstructorDecl_create( BridgedASTContext cContext, BridgedDeclContext cDeclContext, BridgedSourceLoc cInitKeywordLoc, BridgedSourceLoc cFailabilityMarkLoc, _Bool isIUO, void *_Nullable opaqueGenericParams, void *opaqueParameterList, BridgedSourceLoc cAsyncLoc, BridgedSourceLoc cThrowsLoc, + void *_Nullable opaqueThrownType, void *_Nullable opaqueGenericWhereClause); SWIFT_NAME("DestructorDecl_create(astContext:declContext:deinitKeywordLoc:)") @@ -587,6 +589,7 @@ void *EmptyCompositionTypeRepr_create(BridgedASTContext cContext, void *FunctionTypeRepr_create(BridgedASTContext cContext, void *argsTy, BridgedSourceLoc cAsyncLoc, BridgedSourceLoc cThrowsLoc, + void * _Nullable thrownType, BridgedSourceLoc cArrowLoc, void *returnType); void *GenericIdentTypeRepr_create(BridgedASTContext cContext, BridgedIdentifier name, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index dba332e23d3c..5ccdf8682b2d 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6942,6 +6942,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Location of the 'throws' token. SourceLoc ThrowsLoc; + /// The error type that is being thrown. + TypeLoc ThrownType; + struct { unsigned NeedsNewVTableEntryComputed : 1; unsigned NeedsNewVTableEntry : 1; @@ -6950,12 +6953,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { AbstractFunctionDecl(DeclKind Kind, DeclContext *Parent, DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc ThrownTy, bool HasImplicitSelfDecl, GenericParamList *GenericParams) : GenericContext(DeclContextKind::AbstractFunctionDecl, Parent, GenericParams), ValueDecl(Kind, Parent, Name, NameLoc), BodyAndFP(), AsyncLoc(AsyncLoc), - ThrowsLoc(ThrowsLoc) { + ThrowsLoc(ThrowsLoc), ThrownType(ThrownTy) { setBodyKind(BodyKind::None); Bits.AbstractFunctionDecl.HasImplicitSelfDecl = HasImplicitSelfDecl; Bits.AbstractFunctionDecl.Overridden = false; @@ -7053,6 +7057,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } + /// Retrieves the type representation for the thrown type. + TypeRepr *getThrownTypeRepr() const { + return ThrownType.getTypeRepr(); + } + + /// Retrieves the thrown interface type. + Type getThrownInterfaceType() const; + /// Returns if the function throws or is async. bool hasEffect(EffectKind kind) const; @@ -7443,12 +7455,13 @@ class FuncDecl : public AbstractFunctionDecl { DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc ThrownTy, bool HasImplicitSelfDecl, GenericParamList *GenericParams, DeclContext *Parent) : AbstractFunctionDecl(Kind, Parent, Name, NameLoc, Async, AsyncLoc, - Throws, ThrowsLoc, + Throws, ThrowsLoc, ThrownTy, HasImplicitSelfDecl, GenericParams), StaticLoc(StaticLoc), FuncLoc(FuncLoc) { assert(!Name.getBaseName().isSpecial()); @@ -7473,6 +7486,7 @@ class FuncDecl : public AbstractFunctionDecl { DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc ThrownTy, GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN); @@ -7496,6 +7510,7 @@ class FuncDecl : public AbstractFunctionDecl { static FuncDecl *createDeserialized(ASTContext &Context, StaticSpellingKind StaticSpelling, DeclName Name, bool Async, bool Throws, + Type ThrownType, GenericParamList *GenericParams, Type FnRetType, DeclContext *Parent); @@ -7503,6 +7518,7 @@ class FuncDecl : public AbstractFunctionDecl { StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeRepr *ThrownTyR, GenericParamList *GenericParams, ParameterList *BodyParams, TypeRepr *ResultTyR, DeclContext *Parent); @@ -7510,13 +7526,15 @@ class FuncDecl : public AbstractFunctionDecl { static FuncDecl *createImplicit(ASTContext &Context, StaticSpellingKind StaticSpelling, DeclName Name, SourceLoc NameLoc, bool Async, - bool Throws, GenericParamList *GenericParams, + bool Throws, Type ThrownType, + GenericParamList *GenericParams, ParameterList *BodyParams, Type FnRetType, DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, bool Async, - bool Throws, ParameterList *BodyParams, + bool Throws, Type ThrownType, + ParameterList *BodyParams, Type FnRetType, GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN); @@ -7649,11 +7667,12 @@ class AccessorDecl final : public FuncDecl { AccessorKind accessorKind, AbstractStorageDecl *storage, SourceLoc staticLoc, StaticSpellingKind staticSpelling, bool async, SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, + TypeLoc thrownTy, bool hasImplicitSelfDecl, DeclContext *parent) : FuncDecl(DeclKind::Accessor, staticLoc, staticSpelling, /*func loc*/ declLoc, /*name*/ Identifier(), /*name loc*/ declLoc, async, asyncLoc, - throws, throwsLoc, hasImplicitSelfDecl, + throws, throwsLoc, thrownTy, hasImplicitSelfDecl, /*genericParams*/ nullptr, parent), AccessorKeywordLoc(accessorKeywordLoc), Storage(storage) { assert(!async || accessorKind == AccessorKind::Get @@ -7666,6 +7685,7 @@ class AccessorDecl final : public FuncDecl { AccessorKind accessorKind, AbstractStorageDecl *storage, SourceLoc staticLoc, StaticSpellingKind staticSpelling, bool async, SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, + TypeLoc thrownTy, DeclContext *parent, ClangNode clangNode); llvm::Optional getCachedIsTransparent() const { @@ -7682,6 +7702,7 @@ class AccessorDecl final : public FuncDecl { AbstractStorageDecl *storage, StaticSpellingKind staticSpelling, bool async, bool throws, + Type thrownType, Type fnRetType, DeclContext *parent); static AccessorDecl * @@ -7689,7 +7710,8 @@ class AccessorDecl final : public FuncDecl { AccessorKind accessorKind, AbstractStorageDecl *storage, SourceLoc staticLoc, StaticSpellingKind staticSpelling, bool async, SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, - ParameterList *parameterList, Type fnRetType, DeclContext *parent, + TypeLoc thrownType, ParameterList *parameterList, Type fnRetType, + DeclContext *parent, ClangNode clangNode = ClangNode()); SourceLoc getAccessorKeywordLoc() const { return AccessorKeywordLoc; } @@ -8052,6 +8074,7 @@ class ConstructorDecl : public AbstractFunctionDecl { bool Failable, SourceLoc FailabilityLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc thrownTy, ParameterList *BodyParams, GenericParamList *GenericParams, DeclContext *Parent); @@ -8062,6 +8085,7 @@ class ConstructorDecl : public AbstractFunctionDecl { bool failable, SourceLoc failabilityLoc, bool async, SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, + Type thrownTy, ParameterList *bodyParams, GenericParamList *genericParams, DeclContext *parent); diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6e103aba20d4..031eb9309924 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1690,6 +1690,13 @@ ERROR(attr_type_eraser_expected_type_name,none, ERROR(attr_type_eraser_expected_rparen,none, "expected ')' after type name for @_typeEraser", ()) +ERROR(expected_thrown_error_type,none, + "expected thrown error type after 'throws('", ()) +ERROR(expected_rparen_after_thrown_error_type,none, + "expected ')' after thrown error type", ()) +ERROR(rethrows_with_thrown_error,none, + "'rethrows' cannot be combined with a specific thrown error type", ()) + ERROR(attr_private_import_expected_rparen,none, "expected ')' after function name for @_private", ()) ERROR(attr_private_import_expected_sourcefile, none, diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 783f1e88f5fc..79dcf45537c1 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -3981,6 +3981,9 @@ class ClosureExpr : public AbstractClosureExpr { /// The location of the "in", if present. SourceLoc InLoc; + /// The explcitly-specified thrown type. + TypeExpr *ThrownType; + /// The explicitly-specified result type. llvm::PointerIntPair ExplicitResultTypeAndBodyState; @@ -3991,14 +3994,14 @@ class ClosureExpr : public AbstractClosureExpr { ClosureExpr(const DeclAttributes &attributes, SourceRange bracketRange, VarDecl *capturedSelfDecl, ParameterList *params, SourceLoc asyncLoc, SourceLoc throwsLoc, - SourceLoc arrowLoc, SourceLoc inLoc, TypeExpr *explicitResultType, - DeclContext *parent) + TypeExpr *thrownType, SourceLoc arrowLoc, SourceLoc inLoc, + TypeExpr *explicitResultType, DeclContext *parent) : AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false, parent), Attributes(attributes), BracketRange(bracketRange), CapturedSelfDecl(capturedSelfDecl), AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc), - InLoc(inLoc), + InLoc(inLoc), ThrownType(thrownType), ExplicitResultTypeAndBodyState(explicitResultType, BodyState::Parsed), Body(nullptr) { setParameterList(params); @@ -4092,6 +4095,24 @@ class ClosureExpr : public AbstractClosureExpr { return ThrowsLoc; } + /// Retrieve the explicitly-thrown type. + Type getExplicitThrownType() const { + if (ThrownType) + return ThrownType->getInstanceType(); + + return nullptr; + } + + void setExplicitThrownType(Type thrownType); + + /// Retrieve the explicitly-thrown type representation. + TypeRepr *getExplicitThrownTypeRepr() const { + if (ThrownType) + return ThrownType->getTypeRepr(); + + return nullptr; + } + Type getExplicitResultType() const { assert(hasExplicitResultType() && "No explicit result type"); return ExplicitResultTypeAndBodyState.getPointer()->getInstanceType(); @@ -5177,24 +5198,30 @@ class ArrowExpr : public Expr { SourceLoc ArrowLoc; Expr *Args; Expr *Result; + Expr *ThrownType; + public: ArrowExpr(Expr *Args, SourceLoc AsyncLoc, SourceLoc ThrowsLoc, - SourceLoc ArrowLoc, Expr *Result) + Expr *ThrownType, SourceLoc ArrowLoc, Expr *Result) : Expr(ExprKind::Arrow, /*implicit=*/false, Type()), AsyncLoc(AsyncLoc), ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), Args(Args), - Result(Result) + Result(Result), ThrownType(ThrownType) { } - ArrowExpr(SourceLoc AsyncLoc, SourceLoc ThrowsLoc, SourceLoc ArrowLoc) + ArrowExpr(SourceLoc AsyncLoc, SourceLoc ThrowsLoc, Expr *ThrownType, + SourceLoc ArrowLoc) : Expr(ExprKind::Arrow, /*implicit=*/false, Type()), AsyncLoc(AsyncLoc), ThrowsLoc(ThrowsLoc), ArrowLoc(ArrowLoc), - Args(nullptr), Result(nullptr) + Args(nullptr), Result(nullptr), ThrownType(ThrownType) { } Expr *getArgsExpr() const { return Args; } void setArgsExpr(Expr *E) { Args = E; } Expr *getResultExpr() const { return Result; } void setResultExpr(Expr *E) { Result = E; } + Expr *getThrownTypeExpr() const { return ThrownType; } + void setThrownTypeExpr(Expr *E) { ThrownType = E; } + SourceLoc getAsyncLoc() const { return AsyncLoc; } SourceLoc getThrowsLoc() const { return ThrowsLoc; } SourceLoc getArrowLoc() const { return ArrowLoc; } diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 9a8c3b079ab2..aec0dc9ff26f 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -499,13 +499,16 @@ class FunctionTypeRepr : public TypeRepr { TupleTypeRepr *ArgsTy; TypeRepr *RetTy; + TypeRepr *ThrownTy; SourceLoc AsyncLoc; SourceLoc ThrowsLoc; SourceLoc ArrowLoc; public: FunctionTypeRepr(GenericParamList *genericParams, TupleTypeRepr *argsTy, - SourceLoc asyncLoc, SourceLoc throwsLoc, SourceLoc arrowLoc, + SourceLoc asyncLoc, SourceLoc throwsLoc, + TypeRepr *thrownTy, + SourceLoc arrowLoc, TypeRepr *retTy, GenericParamList *patternGenericParams = nullptr, ArrayRef patternSubs = {}, @@ -515,7 +518,7 @@ class FunctionTypeRepr : public TypeRepr { InvocationSubs(invocationSubs), PatternGenericParams(patternGenericParams), PatternSubs(patternSubs), - ArgsTy(argsTy), RetTy(retTy), + ArgsTy(argsTy), RetTy(retTy), ThrownTy(thrownTy), AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc) { } @@ -545,6 +548,7 @@ class FunctionTypeRepr : public TypeRepr { } TupleTypeRepr *getArgsTypeRepr() const { return ArgsTy; } + TypeRepr *getThrownTypeRepr() const { return ThrownTy; } TypeRepr *getResultTypeRepr() const { return RetTy; } bool isAsync() const { return AsyncLoc.isValid(); } bool isThrowing() const { return ThrowsLoc.isValid(); } diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 87db5e5111ad..b3b641b4f24c 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1279,6 +1279,7 @@ class Parser { ParserStatus parseGetEffectSpecifier(ParsedAccessors &accessors, SourceLoc &asyncLoc, SourceLoc &throwsLoc, + TypeRepr *&thrownTy, bool &hasEffectfulGet, AccessorKind currentKind, SourceLoc const& currentLoc); @@ -1623,6 +1624,7 @@ class Parser { bool &reasync, SourceLoc &throws, bool &rethrows, + TypeRepr *&thrownType, TypeRepr *&retType); /// Parse 'async' and 'throws', if present, putting the locations of the @@ -1639,10 +1641,14 @@ class Parser { /// lieu of 'throws'. ParserStatus parseEffectsSpecifiers(SourceLoc existingArrowLoc, SourceLoc &asyncLoc, bool *reasync, - SourceLoc &throwsLoc, bool *rethrows); + SourceLoc &throwsLoc, bool *rethrows, + TypeRepr *&thrownType); + + /// Returns 'true' if \p T is consider a throwing effect specifier. + static bool isThrowsEffectSpecifier(const Token &T); /// Returns 'true' if \p T is considered effects specifier. - bool isEffectsSpecifier(const Token &T); + static bool isEffectsSpecifier(const Token &T); //===--------------------------------------------------------------------===// // Pattern Parsing @@ -1870,6 +1876,7 @@ class Parser { ParameterList *¶ms, SourceLoc &asyncLoc, SourceLoc &throwsLoc, + TypeExpr *&thrownType, SourceLoc &arrowLoc, TypeExpr *&explicitResultType, SourceLoc &inLoc); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index f778373189e0..39260ac921e1 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1450,6 +1450,10 @@ namespace { } } + if (auto thrownTypeRepr = D->getThrownTypeRepr()) { + printRec(thrownTypeRepr, "thrown_type"); + } + if (auto fac = D->getForeignAsyncConvention()) { printRecArbitrary([&](StringRef label) { printHead("foreign_async_convention", ASTNodeColor, label); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 296db40c09f7..940738e9355f 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4404,8 +4404,19 @@ void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { if (AFD->hasThrows()) { if (AFD->getAttrs().hasAttribute()) Printer << " " << tok::kw_rethrows; - else + else { Printer << " " << tok::kw_throws; + + if (auto thrownType = AFD->getThrownInterfaceType()) { + auto errorType = AFD->getASTContext().getErrorExistentialType(); + TypeRepr *thrownTypeRepr = AFD->getThrownTypeRepr(); + if (!errorType || !thrownType->isEqual(errorType) || + (thrownTypeRepr && Options.PreferTypeRepr)) { + TypeLoc thrownTyLoc(thrownTypeRepr, thrownType); + printTypeLoc(thrownTyLoc); + } + } + } } } } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 805f142f2eeb..3888b8a6755e 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -507,6 +507,11 @@ class Traversal : public ASTVisitorgetParameters())) return true; + if (auto *const ThrownTyR = AFD->getThrownTypeRepr()) { + if (doIt(ThrownTyR)) + return true; + } + if (auto *FD = dyn_cast(AFD)) { if (!isa(FD)) if (auto *const TyR = FD->getResultTypeRepr()) @@ -996,6 +1001,11 @@ class Traversal : public ASTVisitorgetParameters())) return nullptr; + if (auto thrownTypeRepr = expr->getExplicitThrownTypeRepr()) { + if (doIt(thrownTypeRepr)) + return nullptr; + } + if (expr->hasExplicitResultType()) { if (doIt(expr->getExplicitResultTypeRepr())) return nullptr; @@ -1086,6 +1096,11 @@ class Traversal : public ASTVisitorsetArgsExpr(Args); } + if (Expr *Thrown = E->getThrownTypeExpr()) { + Thrown = doIt(Thrown); + if (!Thrown) return nullptr; + E->setThrownTypeExpr(Thrown); + } if (Expr *Result = E->getResultExpr()) { Result = doIt(Result); if (!Result) return nullptr; diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 5ce1ff2f7b91..215f5c9e9fac 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -343,7 +343,7 @@ getBuiltinFunction(Identifier Id, ArrayRef argTypes, Type ResType) { DeclName Name(Context, Id, paramList); auto *const FD = FuncDecl::createImplicit( Context, StaticSpellingKind::None, Name, /*NameLoc=*/SourceLoc(), - /*Async=*/false, /*Throws=*/false, + /*Async=*/false, /*Throws=*/false, /*thrownType=*/Type(), /*GenericParams=*/nullptr, paramList, ResType, DC); FD->setAccess(AccessLevel::Public); return FD; @@ -364,7 +364,7 @@ getBuiltinFunctionImpl(SynthesisContext &SC, Identifier id, DeclName name(SC.Context, id, params); auto *FD = FuncDecl::createImplicit( SC.Context, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(), - extInfo.isAsync(), extInfo.isThrowing(), + extInfo.isAsync(), extInfo.isThrowing(), /*thrownType=*/Type(), genericParams, params, resultType, SC.DC); FD->setAccess(AccessLevel::Public); FD->setGenericSignature(signature); @@ -450,7 +450,7 @@ getBuiltinGenericFunction(Identifier Id, Context, StaticSpellingKind::None, Name, /*NameLoc=*/SourceLoc(), Async, - Throws != BuiltinThrowsKind::None, + Throws != BuiltinThrowsKind::None, /*thrownType=*/Type(), GenericParams, paramList, ResType, DC); func->setAccess(AccessLevel::Public); diff --git a/lib/AST/CASTBridging.cpp b/lib/AST/CASTBridging.cpp index 7ede52564cbc..e4c5ea69a6e7 100644 --- a/lib/AST/CASTBridging.cpp +++ b/lib/AST/CASTBridging.cpp @@ -432,7 +432,8 @@ FuncDecl_create(BridgedASTContext cContext, BridgedDeclContext cDeclContext, BridgedIdentifier cName, BridgedSourceLoc cNameLoc, void *_Nullable opaqueGenericParamList, void *opaqueParameterList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, void *_Nullable opaqueReturnType, + BridgedSourceLoc cThrowsLoc, void *_Nullable opaqueThrownType, + void *_Nullable opaqueReturnType, void *_Nullable opaqueGenericWhereClause) { ASTContext &context = convertASTContext(cContext); @@ -440,11 +441,13 @@ FuncDecl_create(BridgedASTContext cContext, BridgedDeclContext cDeclContext, auto declName = DeclName(context, convertIdentifier(cName), paramList); auto asyncLoc = convertSourceLoc(cAsyncLoc); auto throwsLoc = convertSourceLoc(cThrowsLoc); + // FIXME: rethrows auto *decl = FuncDecl::create( context, convertSourceLoc(cStaticLoc), StaticSpellingKind::None, convertSourceLoc(cFuncKeywordLoc), declName, convertSourceLoc(cNameLoc), asyncLoc.isValid(), asyncLoc, throwsLoc.isValid(), throwsLoc, + static_cast(opaqueThrownType), static_cast(opaqueGenericParamList), paramList, static_cast(opaqueReturnType), convertDeclContext(cDeclContext)); @@ -459,6 +462,7 @@ BridgedDeclContextAndDecl ConstructorDecl_create( BridgedSourceLoc cInitKeywordLoc, BridgedSourceLoc cFailabilityMarkLoc, bool isIUO, void *_Nullable opaqueGenericParams, void *opaqueParameterList, BridgedSourceLoc cAsyncLoc, BridgedSourceLoc cThrowsLoc, + void *_Nullable opaqueThrownType, void *_Nullable opaqueGenericWhereClause) { assert((bool)cFailabilityMarkLoc.raw || !isIUO); @@ -470,11 +474,12 @@ BridgedDeclContextAndDecl ConstructorDecl_create( auto asyncLoc = convertSourceLoc(cAsyncLoc); auto throwsLoc = convertSourceLoc(cThrowsLoc); auto failabilityMarkLoc = convertSourceLoc(cFailabilityMarkLoc); + // FIXME: rethrows auto *decl = new (context) ConstructorDecl( declName, convertSourceLoc(cInitKeywordLoc), failabilityMarkLoc.isValid(), failabilityMarkLoc, asyncLoc.isValid(), asyncLoc, throwsLoc.isValid(), - throwsLoc, parameterList, + throwsLoc, static_cast(opaqueThrownType), parameterList, static_cast(opaqueGenericParams), convertDeclContext(cDeclContext)); decl->setTrailingWhereClause( @@ -544,7 +549,8 @@ void *ClosureExpr_create(BridgedASTContext cContext, void *body, auto *out = new (context) ClosureExpr(attributes, bracketRange, nullptr, nullptr, asyncLoc, - throwsLoc, arrowLoc, inLoc, nullptr, declContext); + throwsLoc, /*FIXME:thrownType=*/nullptr, arrowLoc, inLoc, + nullptr, declContext); out->setBody((BraceStmt *)body, true); out->setParameterList(params); return (Expr *)out; @@ -1061,11 +1067,12 @@ void *CompositionTypeRepr_create(BridgedASTContext cContext, void *FunctionTypeRepr_create(BridgedASTContext cContext, void *argsTy, BridgedSourceLoc cAsyncLoc, BridgedSourceLoc cThrowsLoc, + void * _Nullable thrownType, BridgedSourceLoc cArrowLoc, void *returnType) { ASTContext &context = convertASTContext(cContext); return new (context) FunctionTypeRepr( nullptr, (TupleTypeRepr *)argsTy, convertSourceLoc(cAsyncLoc), - convertSourceLoc(cThrowsLoc), convertSourceLoc(cArrowLoc), + convertSourceLoc(cThrowsLoc), (TypeRepr *)thrownType, convertSourceLoc(cArrowLoc), (TypeRepr *)returnType); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4330fd2d9ae1..a2e399094ad8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -945,6 +945,16 @@ bool Decl::preconcurrency() const { return false; } +Type AbstractFunctionDecl::getThrownInterfaceType() const { + if (!hasThrows()) + return Type(); + + // TODO: if there was an explicitly-specified thrown error type + // representation, use that for the computation. + + return getASTContext().getErrorExistentialType(); +} + Expr *AbstractFunctionDecl::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); auto braceStmt = getBody(); @@ -9477,6 +9487,7 @@ FuncDecl *FuncDecl::createImpl(ASTContext &Context, DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc ThrownTy, GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN) { @@ -9488,7 +9499,7 @@ FuncDecl *FuncDecl::createImpl(ASTContext &Context, !ClangN.isNull()); auto D = ::new (DeclPtr) FuncDecl(DeclKind::Func, StaticLoc, StaticSpelling, FuncLoc, - Name, NameLoc, Async, AsyncLoc, Throws, ThrowsLoc, + Name, NameLoc, Async, AsyncLoc, Throws, ThrowsLoc, ThrownTy, HasImplicitSelfDecl, GenericParams, Parent); if (ClangN) D->setClangNode(ClangN); @@ -9501,13 +9512,16 @@ FuncDecl *FuncDecl::createImpl(ASTContext &Context, FuncDecl *FuncDecl::createDeserialized(ASTContext &Context, StaticSpellingKind StaticSpelling, DeclName Name, bool Async, bool Throws, + Type ThrownType, GenericParamList *GenericParams, Type FnRetType, DeclContext *Parent) { assert(FnRetType && "Deserialized result type must not be null"); auto *const FD = FuncDecl::createImpl(Context, SourceLoc(), StaticSpelling, SourceLoc(), Name, SourceLoc(), Async, SourceLoc(), Throws, - SourceLoc(), GenericParams, Parent, ClangNode()); + SourceLoc(), TypeLoc::withoutLoc(ThrownType), + GenericParams, Parent, + ClangNode()); FD->setResultInterfaceType(FnRetType); return FD; } @@ -9516,12 +9530,14 @@ FuncDecl *FuncDecl::create(ASTContext &Context, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeRepr *ThrownTyR, GenericParamList *GenericParams, ParameterList *BodyParams, TypeRepr *ResultTyR, DeclContext *Parent) { auto *const FD = FuncDecl::createImpl( Context, StaticLoc, StaticSpelling, FuncLoc, Name, NameLoc, Async, - AsyncLoc, Throws, ThrowsLoc, GenericParams, Parent, ClangNode()); + AsyncLoc, Throws, ThrowsLoc, ThrownTyR, GenericParams, Parent, + ClangNode()); FD->setParameters(BodyParams); FD->FnRetType = TypeLoc(ResultTyR); return FD; @@ -9530,13 +9546,15 @@ FuncDecl *FuncDecl::create(ASTContext &Context, SourceLoc StaticLoc, FuncDecl *FuncDecl::createImplicit(ASTContext &Context, StaticSpellingKind StaticSpelling, DeclName Name, SourceLoc NameLoc, bool Async, - bool Throws, GenericParamList *GenericParams, + bool Throws, Type ThrownType, + GenericParamList *GenericParams, ParameterList *BodyParams, Type FnRetType, DeclContext *Parent) { assert(FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpelling, SourceLoc(), Name, NameLoc, Async, - SourceLoc(), Throws, SourceLoc(), GenericParams, Parent, ClangNode()); + SourceLoc(), Throws, SourceLoc(), TypeLoc::withoutLoc(ThrownType), + GenericParams, Parent, ClangNode()); FD->setImplicit(); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); @@ -9545,14 +9563,16 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, bool Async, - bool Throws, ParameterList *BodyParams, + bool Throws, Type ThrownType, + ParameterList *BodyParams, Type FnRetType, GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN) { assert(ClangN); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - Async, SourceLoc(), Throws, SourceLoc(), GenericParams, Parent, ClangN); + Async, SourceLoc(), Throws, SourceLoc(), TypeLoc::withoutLoc(ThrownType), + GenericParams, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); return FD; @@ -9581,8 +9601,8 @@ AccessorDecl *AccessorDecl::createImpl( ASTContext &ctx, SourceLoc declLoc, SourceLoc accessorKeywordLoc, AccessorKind accessorKind, AbstractStorageDecl *storage, SourceLoc staticLoc, StaticSpellingKind staticSpelling, bool async, - SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, DeclContext *parent, - ClangNode clangNode) { + SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, TypeLoc thrownType, + DeclContext *parent, ClangNode clangNode) { bool hasImplicitSelfDecl = parent->isTypeContext(); size_t size = sizeof(AccessorDecl) + (hasImplicitSelfDecl ? sizeof(ParamDecl *) @@ -9592,7 +9612,7 @@ AccessorDecl *AccessorDecl::createImpl( auto D = ::new (buffer) AccessorDecl(declLoc, accessorKeywordLoc, accessorKind, storage, staticLoc, staticSpelling, async, asyncLoc, throws, - throwsLoc, hasImplicitSelfDecl, parent); + throwsLoc, thrownType, hasImplicitSelfDecl, parent); if (clangNode) D->setClangNode(clangNode); if (hasImplicitSelfDecl) @@ -9603,13 +9623,13 @@ AccessorDecl *AccessorDecl::createImpl( AccessorDecl *AccessorDecl::createDeserialized( ASTContext &ctx, AccessorKind accessorKind, AbstractStorageDecl *storage, - StaticSpellingKind staticSpelling, bool async, bool throws, Type fnRetType, - DeclContext *parent) { + StaticSpellingKind staticSpelling, bool async, bool throws, Type thrownType, + Type fnRetType, DeclContext *parent) { assert(fnRetType && "Deserialized result type must not be null"); auto *const D = AccessorDecl::createImpl( ctx, SourceLoc(), SourceLoc(), accessorKind, storage, SourceLoc(), - staticSpelling, async, SourceLoc(), throws, SourceLoc(), parent, - ClangNode()); + staticSpelling, async, SourceLoc(), throws, SourceLoc(), + TypeLoc::withoutLoc(thrownType), parent, ClangNode()); D->setResultInterfaceType(fnRetType); return D; } @@ -9620,11 +9640,12 @@ AccessorDecl::create(ASTContext &ctx, SourceLoc declLoc, AbstractStorageDecl *storage, SourceLoc staticLoc, StaticSpellingKind staticSpelling, bool async, SourceLoc asyncLoc, bool throws, SourceLoc throwsLoc, - ParameterList *bodyParams, Type fnRetType, + TypeLoc thrownType, ParameterList *bodyParams, Type fnRetType, DeclContext *parent, ClangNode clangNode) { auto *D = AccessorDecl::createImpl( ctx, declLoc, accessorKeywordLoc, accessorKind, storage, staticLoc, - staticSpelling, async, asyncLoc, throws, throwsLoc, parent, clangNode); + staticSpelling, async, asyncLoc, throws, throwsLoc, thrownType, parent, + clangNode); D->setParameters(bodyParams); D->setResultInterfaceType(fnRetType); return D; @@ -9774,11 +9795,12 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, bool Failable, SourceLoc FailabilityLoc, bool Async, SourceLoc AsyncLoc, bool Throws, SourceLoc ThrowsLoc, + TypeLoc ThrownType, ParameterList *BodyParams, GenericParamList *GenericParams, DeclContext *Parent) : AbstractFunctionDecl(DeclKind::Constructor, Parent, Name, ConstructorLoc, - Async, AsyncLoc, Throws, ThrowsLoc, + Async, AsyncLoc, Throws, ThrowsLoc, ThrownType, /*HasImplicitSelfDecl=*/true, GenericParams), FailabilityLoc(FailabilityLoc), @@ -9797,7 +9819,8 @@ ConstructorDecl *ConstructorDecl::createImported( ASTContext &ctx, ClangNode clangNode, DeclName name, SourceLoc constructorLoc, bool failable, SourceLoc failabilityLoc, bool async, SourceLoc asyncLoc, - bool throws, SourceLoc throwsLoc, ParameterList *bodyParams, + bool throws, SourceLoc throwsLoc, Type thrownType, + ParameterList *bodyParams, GenericParamList *genericParams, DeclContext *parent) { void *declPtr = allocateMemoryForDecl( ctx, sizeof(ConstructorDecl), true); @@ -9805,7 +9828,7 @@ ConstructorDecl *ConstructorDecl::createImported( ConstructorDecl(name, constructorLoc, failable, failabilityLoc, async, asyncLoc, - throws, throwsLoc, + throws, throwsLoc, TypeLoc::withoutLoc(thrownType), bodyParams, genericParams, parent); ctor->setClangNode(clangNode); return ctor; @@ -9829,6 +9852,7 @@ DestructorDecl::DestructorDecl(SourceLoc DestructorLoc, DeclContext *Parent) DeclBaseName::createDestructor(), DestructorLoc, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), /*HasImplicitSelfDecl=*/true, /*GenericParams=*/nullptr), SelfDecl(nullptr) { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index dd3fe9f7ac52..e481838077b9 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2035,6 +2035,13 @@ bool ClosureExpr::hasEmptyBody() const { return getBody()->empty(); } +void ClosureExpr::setExplicitThrownType(Type thrownType) { + assert(thrownType && !thrownType->hasTypeVariable() && + !thrownType->hasPlaceholder()); + assert(ThrownType); + ThrownType->setType(MetatypeType::get(thrownType)); +} + void ClosureExpr::setExplicitResultType(Type ty) { assert(ty && !ty->hasTypeVariable() && !ty->hasPlaceholder()); ExplicitResultTypeAndBodyState.getPointer() diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 2f794ee07aaf..6a971d9ec0e1 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -329,6 +329,13 @@ void FunctionTypeRepr::printImpl(ASTPrinter &Printer, if (isThrowing()) { Printer << " "; Printer.printKeyword("throws", Opts); + + if (ThrownTy) { + // FIXME: Do we need a PrintStructureKind for this? + Printer << "("; + printTypeRepr(ThrownTy, Printer, Opts); + Printer << ")"; + } } Printer << " -> "; Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); diff --git a/lib/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index 5927fd60db33..141e1a323377 100644 --- a/lib/ASTGen/Sources/ASTGen/Decls.swift +++ b/lib/ASTGen/Sources/ASTGen/Decls.swift @@ -255,6 +255,7 @@ extension ASTGenVisitor { parameterList: self.visit(node.signature.parameterClause).rawValue, asyncSpecifierLoc: (node.signature.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), throwsSpecifierLoc: (node.signature.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), + thrownType: self.visit(node.signature.effectSpecifiers?.thrownType?.type)?.rawValue, returnType: self.visit(node.signature.returnClause?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) @@ -279,6 +280,7 @@ extension ASTGenVisitor { parameterList: self.visit(node.signature.parameterClause).rawValue, asyncSpecifierLoc: (node.signature.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), throwsSpecifierLoc: (node.signature.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), + thrownType: self.visit(node.signature.effectSpecifiers?.thrownType?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index fa4efae600d6..97904f1251ad 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -159,6 +159,7 @@ extension ASTGenVisitor { ), (node.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), (node.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), + self.visit(node.effectSpecifiers?.thrownType?.type)?.rawValue, node.returnClause.arrow.bridgedSourceLoc(in: self), visit(node.returnClause.type).rawValue ) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index f2539d9b54ee..e88ddd224d12 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -4981,7 +4981,8 @@ makeBaseClassMemberAccessors(DeclContext *declContext, StaticSpellingKind::None, // TODO: we should handle static vars. /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), bodyParams, computedType, declContext); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + bodyParams, computedType, declContext); getterDecl->setIsTransparent(true); getterDecl->setAccess(AccessLevel::Public); getterDecl->setBodySynthesizer(synthesizeBaseClassFieldGetterBody, @@ -5013,8 +5014,8 @@ makeBaseClassMemberAccessors(DeclContext *declContext, StaticSpellingKind::None, // TODO: we should handle static vars. /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), setterBodyParams, TupleType::getEmpty(ctx), - declContext); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + setterBodyParams, TupleType::getEmpty(ctx),declContext); setterDecl->setIsTransparent(true); setterDecl->setAccess(AccessLevel::Public); setterDecl->setBodySynthesizer(synthesizeBaseClassFieldSetterBody, @@ -5085,6 +5086,7 @@ cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) { auto out = FuncDecl::createImplicit( context, fn->getStaticSpelling(), fn->getName(), fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(), + fn->getThrownInterfaceType(), fn->getGenericParams(), fn->getParameters(), fn->getResultInterfaceType(), newContext); auto inheritedAttributes = cloneImportedAttributes(decl, context); @@ -5961,6 +5963,7 @@ static ValueDecl *rewriteIntegerTypes(SubstitutionMap subst, ValueDecl *oldDecl, func->getASTContext(), func->getNameLoc(), func->getName(), func->getNameLoc(), func->hasAsync(), func->hasThrows(), + /*FIXME:ThrownType=*/func->getThrownInterfaceType(), fixedParams, originalFnSubst->getResult(), /*genericParams=*/nullptr, func->getDeclContext(), newDecl->getClangDecl()); if (func->isStatic()) newFnDecl->setStatic(); @@ -6128,7 +6131,8 @@ static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl, auto newFnDecl = FuncDecl::createImplicit( newDecl->getASTContext(), newDecl->getStaticSpelling(), newDecl->getName(), newDecl->getNameLoc(), newDecl->hasAsync(), - newDecl->hasThrows(), /*genericParams=*/nullptr, fixedParams, + newDecl->hasThrows(), newDecl->getThrownInterfaceType(), + /*genericParams=*/nullptr, fixedParams, fixedResultType, newDecl->getDeclContext()); newFnDecl->copyFormalAccessFrom(newDecl); newFnDecl->setBodySynthesizer(synthesizeDependentTypeThunkParamForwarding, newDecl); @@ -6255,6 +6259,7 @@ static ValueDecl *generateThunkForExtraMetatypes(SubstitutionMap subst, auto thunk = FuncDecl::createImplicit( newDecl->getASTContext(), newDecl->getStaticSpelling(), oldDecl->getName(), newDecl->getNameLoc(), newDecl->hasAsync(), newDecl->hasThrows(), + newDecl->getThrownInterfaceType(), /*genericParams=*/nullptr, newParamList, newDecl->getResultInterfaceType(), newDecl->getDeclContext()); thunk->copyFormalAccessFrom(newDecl); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 844ad28eb32f..05471b48aeb7 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -109,11 +109,12 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc, /*accessorKeywordLoc*/ SourceLoc(), accessorInfo->Kind, accessorInfo->Storage, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::None, async, - /*AsyncLoc=*/SourceLoc(), throws, /*ThrowsLoc=*/SourceLoc(), bodyParams, - resultTy, dc, clangNode); + /*AsyncLoc=*/SourceLoc(), throws, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), bodyParams, resultTy, dc, clangNode); } else { decl = FuncDecl::createImported(impl.SwiftContext, funcLoc, name, nameLoc, - async, throws, bodyParams, resultTy, + async, throws, /*thrownType=*/Type(), + bodyParams, resultTy, genericParams, dc, clangNode); } impl.importSwiftAttrAttributes(decl); @@ -624,7 +625,8 @@ static bool addErrorDomain(NominalTypeDecl *swiftDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, stringTy, swiftDecl); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, stringTy, swiftDecl); getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); getterDecl->setIsTransparent(false); @@ -3522,7 +3524,7 @@ namespace { /*failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), - bodyParams, genericParams, dc); + /*ThrownType=*/TypeLoc(), bodyParams, genericParams, dc); } else { auto resultTy = importedType.getType(); @@ -6045,8 +6047,8 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( decl, AccessLevel::Public, name, /*NameLoc=*/SourceLoc(), failable, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), parameterList, - /*GenericParams=*/nullptr, dc); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + parameterList, /*GenericParams=*/nullptr, dc); result->setImplicitlyUnwrappedOptional(isIUO); result->getASTContext().evaluator.cacheOutput(InitKindRequest{result}, std::move(initKind)); @@ -6559,7 +6561,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/importedName.getErrorInfo().has_value(), - /*ThrowsLoc=*/SourceLoc(), bodyParams, + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), bodyParams, /*GenericParams=*/nullptr, const_cast(dc)); addObjCAttribute(result, selector); diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 93791b804490..f972f5b407e3 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -92,7 +92,8 @@ static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, getterType, importedDecl, clangNode); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, getterType, importedDecl, clangNode); getterDecl->setAccess(AccessLevel::Public); getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); @@ -121,7 +122,8 @@ static AccessorDecl *makeFieldSetterDecl(ClangImporter::Implementation &Impl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, voidTy, importedDecl, clangNode); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, voidTy, importedDecl, clangNode); setterDecl->setIsObjC(false); setterDecl->setIsDynamic(false); setterDecl->setSelfAccessKind(SelfAccessKind::Mutating); @@ -407,7 +409,8 @@ ValueDecl *SwiftDeclSynthesizer::createConstant( /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, type, dc); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, type, dc); func->setStatic(isStatic); func->setAccess(getOverridableAccessLevel(dc)); func->setIsObjC(false); @@ -488,7 +491,8 @@ SwiftDeclSynthesizer::createDefaultConstructor(NominalTypeDecl *structDecl) { ConstructorDecl(name, structDecl->getLoc(), /*Failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), emptyPL, + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), emptyPL, /*GenericParams=*/nullptr, structDecl); constructor->setAccess(AccessLevel::Public); @@ -618,7 +622,8 @@ ConstructorDecl *SwiftDeclSynthesizer::createValueConstructor( ConstructorDecl(name, structDecl->getLoc(), /*Failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), paramList, + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), paramList, /*GenericParams=*/nullptr, structDecl); constructor->setAccess(AccessLevel::Public); @@ -1262,7 +1267,8 @@ SwiftDeclSynthesizer::makeEnumRawValueConstructor(EnumDecl *enumDecl) { ConstructorDecl(name, enumDecl->getLoc(), /*Failable=*/true, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), paramPL, + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), paramPL, /*GenericParams=*/nullptr, enumDecl); ctorDecl->setImplicit(); ctorDecl->setAccess(AccessLevel::Public); @@ -1334,7 +1340,8 @@ void SwiftDeclSynthesizer::makeEnumRawValueGetter(EnumDecl *enumDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, rawTy, enumDecl); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, rawTy, enumDecl); getterDecl->setImplicit(); getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); @@ -1398,7 +1405,8 @@ AccessorDecl *SwiftDeclSynthesizer::makeStructRawValueGetter( /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, computedType, structDecl); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, computedType, structDecl); getterDecl->setImplicit(); getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); @@ -1427,7 +1435,8 @@ AccessorDecl *SwiftDeclSynthesizer::buildSubscriptGetterDecl( /*StaticLoc=*/SourceLoc(), subscript->getStaticSpelling(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), params, elementTy, dc, getter->getClangNode()); + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, elementTy, dc, getter->getClangNode()); thunk->setAccess(getOverridableAccessLevel(dc)); @@ -1470,7 +1479,8 @@ AccessorDecl *SwiftDeclSynthesizer::buildSubscriptSetterDecl( /*StaticLoc=*/SourceLoc(), subscript->getStaticSpelling(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), valueIndicesPL, TupleType::getEmpty(C), dc, + /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + valueIndicesPL, TupleType::getEmpty(C), dc, setter->getClangNode()); thunk->setAccess(getOverridableAccessLevel(dc)); @@ -1616,7 +1626,8 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter, ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get, subscript, SourceLoc(), subscript->getStaticSpelling(), /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), bodyParams, elementTy, dc); + /*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(), + bodyParams, elementTy, dc); getterDecl->setAccess(AccessLevel::Public); getterDecl->setImplicit(); getterDecl->setIsDynamic(false); @@ -1643,8 +1654,8 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter, ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set, subscript, SourceLoc(), subscript->getStaticSpelling(), /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), setterParamList, - TupleType::getEmpty(ctx), dc); + /*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(), + setterParamList, TupleType::getEmpty(ctx), dc); setterDecl->setAccess(AccessLevel::Public); setterDecl->setImplicit(); setterDecl->setIsDynamic(false); @@ -1696,8 +1707,8 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter, ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None, /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), ParameterList::createEmpty(ctx), elementTy, - dc); + /*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(), + ParameterList::createEmpty(ctx), elementTy, dc); getterDecl->setAccess(AccessLevel::Public); getterDecl->setImplicit(); getterDecl->setIsDynamic(false); @@ -1728,8 +1739,8 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter, ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set, result, SourceLoc(), StaticSpellingKind::None, /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), setterParamList, - TupleType::getEmpty(ctx), dc); + /*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(), + setterParamList, TupleType::getEmpty(ctx), dc); setterDecl->setAccess(AccessLevel::Public); setterDecl->setImplicit(); setterDecl->setIsDynamic(false); @@ -1825,7 +1836,7 @@ FuncDecl *SwiftDeclSynthesizer::makeSuccessorFunc(FuncDecl *incrementFunc) { auto result = FuncDecl::createImplicit( ctx, StaticSpellingKind::None, name, SourceLoc(), - /*Async*/ false, /*Throws*/ false, + /*Async*/ false, /*Throws*/ false, /*ThrownType=*/Type(), /*GenericParams*/ nullptr, params, returnTy, dc); result->setAccess(AccessLevel::Public); @@ -1947,8 +1958,8 @@ SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod, auto topLevelStaticFuncDecl = FuncDecl::createImplicit( ctx, StaticSpellingKind::None, opDeclName, SourceLoc(), - /*Async*/ false, /*Throws*/ false, genericParamList, - ParameterList::create(ctx, newParams), + /*Async*/ false, /*Throws*/ false, /*ThrownType=*/Type(), + genericParamList, ParameterList::create(ctx, newParams), operatorMethod->getResultInterfaceType(), parentCtx); topLevelStaticFuncDecl->setAccess(AccessLevel::Public); @@ -2027,7 +2038,8 @@ SwiftDeclSynthesizer::makeComputedPropertyFromCXXMethods(FuncDecl *getter, ctx, getter->getLoc(), getter->getLoc(), AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None, /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), ParameterList::createEmpty(ctx), + /*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(), + ParameterList::createEmpty(ctx), getter->getResultInterfaceType(), dc); getterDecl->setAccess(AccessLevel::Public); getterDecl->setImplicit(); @@ -2053,8 +2065,8 @@ SwiftDeclSynthesizer::makeComputedPropertyFromCXXMethods(FuncDecl *getter, ctx, setter->getLoc(), setter->getLoc(), AccessorKind::Set, result, SourceLoc(), StaticSpellingKind::None, /*async*/ false, SourceLoc(), - /*throws*/ false, SourceLoc(), setterParamList, - setter->getResultInterfaceType(), dc); + /*throws*/ false, SourceLoc(), /*thrownType*/ TypeLoc(), + setterParamList, setter->getResultInterfaceType(), dc); setterDecl->setAccess(AccessLevel::Public); setterDecl->setImplicit(); setterDecl->setIsDynamic(false); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 10251f3e5aef..f76a3d161a27 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -7122,7 +7122,7 @@ static AccessorDecl *createAccessorFunc( SourceLoc DeclLoc, ParameterList *param, ParameterList *Indices, SourceLoc StaticLoc, Parser::ParseDeclOptions Flags, AccessorKind Kind, AbstractStorageDecl *storage, Parser *P, SourceLoc AccessorKeywordLoc, - SourceLoc asyncLoc, SourceLoc throwsLoc) { + SourceLoc asyncLoc, SourceLoc throwsLoc, TypeRepr *thrownTy) { // First task, set up the value argument list. This is the "newValue" name // (for setters) followed by the index list (for subscripts). For // non-subscript getters, this degenerates down to "()". @@ -7181,7 +7181,8 @@ static AccessorDecl *createAccessorFunc( P->Context, /*FIXME FuncLoc=*/DeclLoc, AccessorKeywordLoc, Kind, storage, StaticLoc, StaticSpellingKind::None, asyncLoc.isValid(), asyncLoc, - throwsLoc.isValid(), throwsLoc, ValueArg, Type(), P->CurDeclContext); + throwsLoc.isValid(), throwsLoc, thrownTy, ValueArg, Type(), + P->CurDeclContext); return D; } @@ -7450,6 +7451,7 @@ static bool parseAccessorIntroducer(Parser &P, ParserStatus Parser::parseGetEffectSpecifier(ParsedAccessors &accessors, SourceLoc &asyncLoc, SourceLoc &throwsLoc, + TypeRepr *&thrownTy, bool &hasEffectfulGet, AccessorKind currentKind, SourceLoc const& currentLoc) { @@ -7460,7 +7462,7 @@ ParserStatus Parser::parseGetEffectSpecifier(ParsedAccessors &accessors, Status |= parseEffectsSpecifiers(/*existingArrowLoc*/ SourceLoc(), asyncLoc, /*reasync*/ nullptr, throwsLoc, - /*rethrows*/ nullptr); + /*rethrows*/ nullptr, thrownTy); // If we've previously parsed a non-'get' accessor, raise diagnostics, // because we're about to add an effectful 'get' accessor. @@ -7505,13 +7507,15 @@ bool Parser::parseAccessorAfterIntroducer( // on 'get' accessors, we also emit diagnostics if they show up on others. SourceLoc asyncLoc; SourceLoc throwsLoc; - Status |= parseGetEffectSpecifier(accessors, asyncLoc, throwsLoc, + TypeRepr *thrownTy = nullptr; + Status |= parseGetEffectSpecifier(accessors, asyncLoc, throwsLoc, thrownTy, hasEffectfulGet, Kind, Loc); // Set up a function declaration. auto accessor = createAccessorFunc(Loc, ValueNamePattern, Indices, StaticLoc, Flags, - Kind, storage, this, Loc, asyncLoc, throwsLoc); + Kind, storage, this, Loc, asyncLoc, throwsLoc, + thrownTy); accessor->getAttrs() = Attributes; // Collect this accessor and detect conflicts. @@ -7601,7 +7605,8 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices, createAccessorFunc(Tok.getLoc(), /*ValueNamePattern*/ nullptr, Indices, StaticLoc, Flags, AccessorKind::Get, storage, this, /*AccessorKeywordLoc*/ SourceLoc(), - /*asyncLoc*/ SourceLoc(), /*throwsLoc*/ SourceLoc()); + /*asyncLoc*/ SourceLoc(), /*throwsLoc*/ SourceLoc(), + /*thrownTy*/ nullptr); accessors.add(getter); parseAbstractFunctionBody(getter); accessors.RBLoc = getter->getEndLoc(); @@ -8448,10 +8453,11 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, bool reasync; SourceLoc throwsLoc; bool rethrows; + TypeRepr *thrownTy = nullptr; Status |= parseFunctionSignature(SimpleName, FullName, BodyParams, DefaultArgs, asyncLoc, reasync, - throwsLoc, rethrows, + throwsLoc, rethrows, thrownTy, FuncRetTy); if (Status.hasCodeCompletion() && !CodeCompletionCallbacks) { // Trigger delayed parsing, no need to continue. @@ -8478,7 +8484,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, FuncLoc, FullName, NameLoc, /*Async=*/isAsync, asyncLoc, /*Throws=*/throwsLoc.isValid(), throwsLoc, - GenericParams, + thrownTy, GenericParams, BodyParams, FuncRetTy, CurDeclContext); @@ -9553,11 +9559,12 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { bool reasync; SourceLoc throwsLoc; bool rethrows; + TypeRepr *thrownTy = nullptr; Status |= parseFunctionSignature(DeclBaseName::createConstructor(), FullName, BodyParams, DefaultArgs, asyncLoc, reasync, - throwsLoc, rethrows, + throwsLoc, rethrows, thrownTy, FuncRetTy); if (Status.hasCodeCompletion() && !CodeCompletionCallbacks) { // Trigger delayed parsing, no need to continue. @@ -9602,7 +9609,7 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { Failable, FailabilityLoc, isAsync, asyncLoc, throwsLoc.isValid(), throwsLoc, - BodyParams, GenericParams, + thrownTy, BodyParams, GenericParams, CurDeclContext); CD->setImplicitlyUnwrappedOptional(IUO); CD->getAttrs() = Attributes; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 4c99fc97e08b..da4b921e1042 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -124,14 +124,16 @@ ParserResult Parser::parseExprAs() { /// parseExprArrow /// /// expr-arrow: -/// 'async'? 'throws'? '->' +/// 'async'? ('throws' ('(' type ')')?)? '->' ParserResult Parser::parseExprArrow() { SourceLoc asyncLoc, throwsLoc, arrowLoc; ParserStatus status; + TypeRepr *thrownTyRepr = nullptr; status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, /*reasync=*/nullptr, - throwsLoc, /*rethrows=*/nullptr); + throwsLoc, /*rethrows=*/nullptr, + thrownTyRepr); if (status.hasCodeCompletion() && !CodeCompletionCallbacks) { // Trigger delayed parsing, no need to continue. return status; @@ -149,9 +151,15 @@ ParserResult Parser::parseExprArrow() { parseEffectsSpecifiers(arrowLoc, asyncLoc, /*reasync=*/nullptr, - throwsLoc, /*rethrows=*/nullptr); + throwsLoc, /*rethrows=*/nullptr, + thrownTyRepr); - auto arrow = new (Context) ArrowExpr(asyncLoc, throwsLoc, arrowLoc); + Expr *thrownTy = nullptr; + if (thrownTyRepr) { + thrownTy = new (Context) TypeExpr(thrownTyRepr); + } + + auto arrow = new (Context) ArrowExpr(asyncLoc, throwsLoc, thrownTy, arrowLoc); return makeParserResult(arrow); } @@ -2523,7 +2531,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( SmallVectorImpl &captureList, VarDecl *&capturedSelfDecl, ParameterList *¶ms, - SourceLoc &asyncLoc, SourceLoc &throwsLoc, + SourceLoc &asyncLoc, SourceLoc &throwsLoc, TypeExpr *&thrownType, SourceLoc &arrowLoc, TypeExpr *&explicitResultType, SourceLoc &inLoc) { // Clear out result parameters. @@ -2532,6 +2540,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( capturedSelfDecl = nullptr; params = nullptr; throwsLoc = SourceLoc(); + thrownType = nullptr; arrowLoc = SourceLoc(); explicitResultType = nullptr; inLoc = SourceLoc(); @@ -2539,15 +2548,22 @@ ParserStatus Parser::parseClosureSignatureIfPresent( // Consume 'async', 'throws', and 'rethrows', but in any order. auto consumeEffectsSpecifiers = [&] { while (isEffectsSpecifier(Tok) || - (Tok.is(tok::code_complete) && !Tok.isAtStartOfLine())) + (Tok.is(tok::code_complete) && !Tok.isAtStartOfLine())) { + bool isThrows = isThrowsEffectSpecifier(Tok); + consumeToken(); + + if (isThrows && Tok.is(tok::l_paren)) + skipSingle(); + } }; // If we have a leading token that may be part of the closure signature, do a // speculative parse to validate it and look for 'in'. if (Tok.isAny( tok::at_sign, tok::l_paren, tok::l_square, tok::identifier, - tok::kw__, tok::code_complete)) { + tok::kw__, tok::code_complete) || + (Tok.is(tok::kw_throws) && peekToken().is(tok::l_paren))) { BacktrackingScope backtrack(*this); // Consume attributes. @@ -2605,7 +2621,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( consumeEffectsSpecifiers(); } } - + // Parse the 'in' at the end. if (Tok.isNot(tok::kw_in)) return makeParserSuccess(); @@ -2817,9 +2833,12 @@ ParserStatus Parser::parseClosureSignatureIfPresent( params = ParameterList::create(Context, elements); } + TypeRepr *thrownTypeRepr = nullptr; + status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, /*reasync*/nullptr, - throwsLoc, /*rethrows*/nullptr); + throwsLoc, /*rethrows*/nullptr, + thrownTypeRepr); // Parse the optional explicit return type. if (Tok.is(tok::arrow)) { @@ -2839,9 +2858,12 @@ ParserStatus Parser::parseClosureSignatureIfPresent( // Check for 'throws' and 'rethrows' after the type and correct it. parseEffectsSpecifiers(arrowLoc, asyncLoc, /*reasync*/nullptr, - throwsLoc, /*rethrows*/nullptr); + throwsLoc, /*rethrows*/nullptr, thrownTypeRepr); } } + + if (thrownTypeRepr) + thrownType = new (Context) TypeExpr(thrownTypeRepr); } // Parse the 'in'. @@ -2943,17 +2965,18 @@ ParserResult Parser::parseExprClosure() { ParameterList *params = nullptr; SourceLoc asyncLoc; SourceLoc throwsLoc; + TypeExpr *thrownType; SourceLoc arrowLoc; TypeExpr *explicitResultType; SourceLoc inLoc; Status |= parseClosureSignatureIfPresent( attributes, bracketRange, captureList, capturedSelfDecl, params, asyncLoc, - throwsLoc, arrowLoc, explicitResultType, inLoc); + throwsLoc, thrownType, arrowLoc, explicitResultType, inLoc); // Create the closure expression and enter its context. auto *closure = new (Context) ClosureExpr( attributes, bracketRange, capturedSelfDecl, params, asyncLoc, throwsLoc, - arrowLoc, inLoc, explicitResultType, CurDeclContext); + thrownType, arrowLoc, inLoc, explicitResultType, CurDeclContext); ParseFunctionBody cc(*this, closure); // Handle parameters. diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 8d5da84a4a08..7e51b987cd5b 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -802,6 +802,7 @@ Parser::parseFunctionSignature(DeclBaseName SimpleName, bool &reasync, SourceLoc &throwsLoc, bool &rethrows, + TypeRepr *&thrownType, TypeRepr *&retType) { SmallVector NamePieces; ParserStatus Status; @@ -818,9 +819,10 @@ Parser::parseFunctionSignature(DeclBaseName SimpleName, // Check for the 'async' and 'throws' keywords. reasync = false; rethrows = false; + thrownType = nullptr; Status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, &reasync, - throwsLoc, &rethrows); + throwsLoc, &rethrows, thrownType); // If there's a trailing arrow, parse the rest as the result type. SourceLoc arrowLoc; @@ -834,7 +836,8 @@ Parser::parseFunctionSignature(DeclBaseName SimpleName, // Check for effect specifiers after the arrow, but before the return type, // and correct it. - parseEffectsSpecifiers(arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows); + parseEffectsSpecifiers(arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows, + thrownType); ParserResult ResultType = parseDeclResultType(diag::expected_type_function_result); @@ -844,7 +847,8 @@ Parser::parseFunctionSignature(DeclBaseName SimpleName, return Status; // Check for effect specifiers after the type and correct it. - parseEffectsSpecifiers(arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows); + parseEffectsSpecifiers( + arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows, thrownType); } else { // Otherwise, we leave retType null. retType = nullptr; @@ -853,6 +857,11 @@ Parser::parseFunctionSignature(DeclBaseName SimpleName, return Status; } +bool Parser::isThrowsEffectSpecifier(const Token &T) { + return T.isAny(tok::kw_throws, tok::kw_rethrows) || + (T.isAny(tok::kw_throw, tok::kw_try) && !T.isAtStartOfLine()); +} + bool Parser::isEffectsSpecifier(const Token &T) { // NOTE: If this returns 'true', that token must be handled in // 'parseEffectsSpecifiers()'. @@ -862,8 +871,7 @@ bool Parser::isEffectsSpecifier(const Token &T) { T.isContextualKeyword("reasync")) return true; - if (T.isAny(tok::kw_throws, tok::kw_rethrows) || - (T.isAny(tok::kw_throw, tok::kw_try) && !T.isAtStartOfLine())) + if (isThrowsEffectSpecifier(T)) return true; return false; @@ -873,9 +881,9 @@ ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc, SourceLoc &asyncLoc, bool *reasync, SourceLoc &throwsLoc, - bool *rethrows) { + bool *rethrows, + TypeRepr *&thrownType) { ParserStatus status; - while (true) { // 'async' bool isReasync = (shouldParseExperimentalConcurrency() && @@ -927,8 +935,7 @@ ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc, } // 'throws'/'rethrows', or diagnose 'throw'/'try'. - if (Tok.isAny(tok::kw_throws, tok::kw_rethrows) || - (Tok.isAny(tok::kw_throw, tok::kw_try) && !Tok.isAtStartOfLine())) { + if (isThrowsEffectSpecifier(Tok)) { bool isRethrows = Tok.is(tok::kw_rethrows); if (throwsLoc.isValid()) { @@ -955,6 +962,31 @@ ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc, throwsLoc = Tok.getLoc(); } consumeToken(); + + // Parse the thrown error type. + SourceLoc lParenLoc; + if (consumeIf(tok::l_paren, lParenLoc)) { + ParserResult parsedThrownTy = + parseType(diag::expected_thrown_error_type); + thrownType = parsedThrownTy.getPtrOrNull(); + status |= parsedThrownTy; + + SourceLoc rParenLoc; + parseMatchingToken( + tok::r_paren, rParenLoc, + diag::expected_rparen_after_thrown_error_type, lParenLoc); + + if (isRethrows) { + diagnose(throwsLoc, diag::rethrows_with_thrown_error) + .highlight(SourceRange(lParenLoc, rParenLoc)); + + isRethrows = false; + + if (rethrows) + *rethrows = false; + } + } + continue; } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 2346e4169392..8b7ee6fdb951 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1081,6 +1081,7 @@ ParserResult Parser::parseStmtDefer() { Context, StaticSpellingKind::None, name, /*NameLoc=*/PreviousLoc, /*Async=*/false, /*Throws=*/false, + /*ThrownType=*/Type(), /*GenericParams*/ nullptr, params, TupleType::getEmpty(Context), CurDeclContext); ParserStatus Status; diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 3d6d9b34f8bc..982a07352bf2 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -445,10 +445,12 @@ ParserResult Parser::parseTypeScalar( // useful diagnostic when parsing a function decl. SourceLoc asyncLoc; SourceLoc throwsLoc; + TypeRepr *thrownTy = nullptr; if (isAtFunctionTypeArrow()) { status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, /*reasync=*/nullptr, - throwsLoc, /*rethrows=*/nullptr); + throwsLoc, /*rethrows=*/nullptr, + thrownTy); } // Handle type-function if we have an arrow. @@ -458,7 +460,8 @@ ParserResult Parser::parseTypeScalar( // Handle async/throws in the wrong place. parseEffectsSpecifiers(arrowLoc, asyncLoc, /*reasync=*/nullptr, - throwsLoc, /*rethrows=*/nullptr); + throwsLoc, /*rethrows=*/nullptr, + thrownTy); ParserResult SecondHalf = parseTypeScalar(diag::expected_type_function_result, @@ -553,7 +556,7 @@ ParserResult Parser::parseTypeScalar( } tyR = new (Context) FunctionTypeRepr(generics, argsTyR, asyncLoc, throwsLoc, - arrowLoc, SecondHalf.get(), + thrownTy, arrowLoc, SecondHalf.get(), patternGenerics, patternSubsTypes, invocationSubsTypes); } else if (auto firstGenerics = generics ? generics : patternGenerics) { @@ -1654,9 +1657,15 @@ bool Parser::canParseType() { if (isAtFunctionTypeArrow()) { // Handle type-function if we have an '->' with optional // 'async' and/or 'throws'. - while (isEffectsSpecifier(Tok)) + while (isEffectsSpecifier(Tok)) { + bool isThrows = isThrowsEffectSpecifier(Tok); consumeToken(); + if (isThrows && Tok.is(tok::l_paren)) { + skipSingle(); + } + } + if (!consumeIf(tok::arrow)) return false; @@ -1787,6 +1796,12 @@ bool Parser::isAtFunctionTypeArrow() { if (isEffectsSpecifier(Tok)) { if (peekToken().is(tok::arrow)) return true; + if (isThrowsEffectSpecifier(Tok) && peekToken().is(tok::l_paren)) { + BacktrackingScope backtrack(*this); + consumeToken(); + skipSingle(); + return isAtFunctionTypeArrow(); + } if (isEffectsSpecifier(peekToken())) { BacktrackingScope backtrack(*this); consumeToken(); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 02afa1b9bdf8..36152d622b01 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1481,7 +1481,8 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { getASTContext(), DeclBaseName(getASTContext().getIdentifier("_asyncMainDrainQueue")), /*Arguments*/ emptyParams), - {}, /*async*/ false, /*throws*/ false, {}, emptyParams, + {}, /*async*/ false, /*throws*/ false, /*thrownType*/Type(), {}, + emptyParams, getASTContext().getNeverType(), moduleDecl); drainQueueFuncDecl->getAttrs().add(new (getASTContext()) SILGenNameAttr( "swift_task_asyncMainDrainQueue", /*raw*/ false, /*implicit*/ true)); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 7408c6c0890a..bfaa7d6005ef 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -1462,8 +1462,8 @@ SILValue SILGenFunction::emitMainExecutor(SILLocation loc) { ctx, DeclBaseName(ctx.getIdentifier("_getMainExecutor")), /*Arguments*/ emptyParams), - {}, /*async*/ false, /*throws*/ false, {}, emptyParams, - ctx.TheExecutorType, + {}, /*async*/ false, /*throws*/ false, /*thrownType*/Type(), {}, + emptyParams, ctx.TheExecutorType, getModule().getSwiftModule()); getMainExecutorFuncDecl->getAttrs().add( new (ctx) SILGenNameAttr("swift_task_getMainExecutor", /*raw*/ false, diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 1f8b711fec16..683e1f8fc2cb 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -329,6 +329,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, /*Failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), paramList, /*GenericParams=*/nullptr, decl); // Mark implicit. @@ -709,6 +710,11 @@ createDesignatedInitOverride(ClassDecl *classDecl, bodyParam->setInterfaceType(substTy); } + Type thrownType; + if (auto superThrownType = superclassCtor->getThrownInterfaceType()) { + thrownType = superThrownType.subst(subMap); + } + // Create the initializer declaration, inheriting the name, // failability, and throws from the superclass initializer. auto implCtx = classDecl->getImplementationContext()->getAsGenericContext(); @@ -721,6 +727,7 @@ createDesignatedInitOverride(ClassDecl *classDecl, /*AsyncLoc=*/SourceLoc(), /*Throws=*/superclassCtor->hasThrows(), /*ThrowsLoc=*/SourceLoc(), + TypeLoc::withoutLoc(thrownType), bodyParams, genericParams, implCtx); ctor->setImplicit(); diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 31748c5926f2..6521952b1057 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -727,8 +727,8 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) { FuncDecl *thunk = FuncDecl::createImplicit( C, swift::StaticSpellingKind::None, thunkName, SourceLoc(), - /*async=*/true, /*throws=*/true, genericParamList, params, - func->getResultInterfaceType(), DC); + /*async=*/true, /*throws=*/true, /*thrownType=*/Type(), + genericParamList, params, func->getResultInterfaceType(), DC); assert(thunk && "couldn't create a distributed thunk"); diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 9e18da410776..978523b4e037 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -269,7 +269,7 @@ class DebuggerTestingTransform : public ASTWalker { auto *Params = ParameterList::createEmpty(Ctx); auto *Closure = new (Ctx) ClosureExpr(DeclAttributes(), SourceRange(), nullptr, Params, - SourceLoc(), SourceLoc(), + SourceLoc(), SourceLoc(), /*thrownType=*/nullptr, SourceLoc(), SourceLoc(), nullptr, getCurrentDeclContext()); Closure->setImplicit(true); diff --git a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp index 29379d381b32..f637f71281ab 100644 --- a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp +++ b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp @@ -191,6 +191,7 @@ static ValueDecl *deriveMathOperator(DerivedConformance &derived, /*NameLoc=*/SourceLoc(), /*Async=*/false, /*Throws=*/false, + /*ThrownType=*/Type(), /*GenericParams=*/nullptr, params, selfInterfaceType, parentDC); auto bodySynthesizer = [](AbstractFunctionDecl *funcDecl, void *ctx) -> std::pair { diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 98d9920620ba..ee042850456d 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -1240,7 +1240,8 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { auto *const encodeDecl = FuncDecl::createImplicit( C, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(), /*Async=*/false, - /*Throws=*/true, /*GenericParams=*/nullptr, params, returnType, + /*Throws=*/true, /*ThrownType=*/Type(), + /*GenericParams=*/nullptr, params, returnType, conformanceDC); encodeDecl->setSynthesized(); @@ -1891,7 +1892,8 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { new (C) ConstructorDecl(name, SourceLoc(), /*Failable=*/false,SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/true, SourceLoc(), paramList, + /*Throws=*/true, SourceLoc(), + /*ThrownType=*/TypeLoc(), paramList, /*GenericParams=*/nullptr, conformanceDC); initDecl->setImplicit(); initDecl->setSynthesized(); diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index 635e152e421a..f02ca266442d 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -131,6 +131,7 @@ static ValueDecl *deriveInitDecl(DerivedConformance &derived, Type paramType, /*Failable=*/true, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*ThrownType=*/TypeLoc(), paramList, /*GenericParams=*/nullptr, parentDC); diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index 22f9367f7426..526949ad356f 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -253,6 +253,7 @@ deriveComparable_lt( C, StaticSpellingKind::KeywordStatic, name, /*NameLoc=*/SourceLoc(), /*Async=*/false, /*Throws=*/false, + /*ThrownType=*/Type(), /*GenericParams=*/nullptr, params, boolTy, parentDC); comparableDecl->setUserAccessible(false); diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index fbeba88e4f8a..852c54e624a3 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -317,6 +317,7 @@ static ValueDecl *deriveDifferentiable_method( C, StaticSpellingKind::None, declName, /*NameLoc=*/SourceLoc(), /*Async=*/false, /*Throws=*/false, + /*ThrownType=*/Type(), /*GenericParams=*/nullptr, params, returnType, parentDC); funcDecl->setSynthesized(); if (!nominal->getSelfClassDecl()) diff --git a/lib/Sema/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformanceDistributedActor.cpp index 20d893565c37..216ffadd1878 100644 --- a/lib/Sema/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformanceDistributedActor.cpp @@ -108,6 +108,7 @@ static FuncDecl *deriveDistributedActor_resolve(DerivedConformance &derived) { name, SourceLoc(), /*async=*/false, /*throws=*/true, + /*ThrownType=*/Type(), /*genericParams=*/nullptr, params, /*returnType*/decl->getDeclaredInterfaceType(), @@ -257,7 +258,9 @@ static FuncDecl* createLocalFunc_doInvokeOnReturn( DeclName(C, doInvokeLocalFuncIdent, doInvokeParamsList), sloc, /*async=*/true, - /*throws=*/true, doInvokeGenericParamList, doInvokeParamsList, + /*throws=*/true, + /*ThrownType=*/Type(), + doInvokeGenericParamList, doInvokeParamsList, /*returnType=*/C.TheEmptyTupleType, parentFunc); doInvokeOnReturnFunc->setImplicit(); doInvokeOnReturnFunc->setSynthesized(); @@ -419,6 +422,7 @@ static FuncDecl *deriveDistributedActorSystem_invokeHandlerOnReturn( FuncDecl::createImplicit(C, StaticSpellingKind::None, name, SourceLoc(), /*async=*/true, /*throws=*/true, + /*ThrownType=*/Type(), /*genericParams=*/nullptr, params, /*returnType*/ TupleType::getEmpty(C), system); funcDecl->setSynthesized(true); diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 11517a4c87ec..2940eaadc1d4 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -417,7 +417,7 @@ deriveEquatable_eq( auto *const eqDecl = FuncDecl::createImplicit( C, StaticSpellingKind::KeywordStatic, name, /*NameLoc=*/SourceLoc(), /*Async=*/false, - /*Throws=*/false, + /*Throws=*/false, /*ThrownType=*/Type(), /*GenericParams=*/nullptr, params, boolTy, parentDC); eqDecl->setUserAccessible(false); @@ -554,7 +554,7 @@ deriveHashable_hashInto( auto *const hashDecl = FuncDecl::createImplicit( C, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(), /*Async=*/false, - /*Throws=*/false, + /*Throws=*/false, /*ThrownType=*/Type(), /*GenericParams=*/nullptr, params, returnType, parentDC); hashDecl->setBodySynthesizer(bodySynthesizer); hashDecl->copyFormalAccessFrom(derived.Nominal, @@ -913,7 +913,8 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { AccessorKind::Get, hashValueDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), params, intType, parentDC); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, intType, parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue); getterDecl->setSynthesized(); diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index 68d36ea04309..3d2384c41660 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -429,7 +429,7 @@ deriveRawRepresentable_init(DerivedConformance &derived) { /*Failable=*/ true, /*FailabilityLoc=*/SourceLoc(), /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), - paramList, + /*ThrownType=*/TypeLoc(), paramList, /*GenericParams=*/nullptr, parentDC); initDecl->setImplicit(); diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index d8d7efc3c6ee..e735161a0f36 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -531,8 +531,8 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, AccessorKind::Get, property, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), params, - property->getInterfaceType(), parentDC); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, property->getInterfaceType(), parentDC); getterDecl->setImplicit(); getterDecl->setIsTransparent(false); getterDecl->copyFormalAccessFrom(property); diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 66abb7dc2ba9..f55f72caf8ac 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -2014,6 +2014,12 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { TupleTypeRepr::create(Ctx, {ErrRepr}, ArgRange); } + TypeRepr *ThrownTypeRepr = nullptr; + if (auto thrownTypeExpr = AE->getThrownTypeExpr()) { + ThrownTypeRepr = extractTypeRepr(thrownTypeExpr); + assert(ThrownTypeRepr && "Parser ensures that this never fails"); + } + TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); if (!ResultTypeRepr) { Ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), @@ -2024,7 +2030,8 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { auto NewTypeRepr = new (Ctx) FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(), - AE->getThrowsLoc(), AE->getArrowLoc(), ResultTypeRepr); + AE->getThrowsLoc(), ThrownTypeRepr, AE->getArrowLoc(), + ResultTypeRepr); return new (Ctx) TypeExpr(NewTypeRepr); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d559dc086d3c..e0a722250d6f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2648,6 +2648,7 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, /*NameLoc=*/SourceLoc(), /*Async=*/mainFunction->hasAsync(), /*Throws=*/mainFunction->hasThrows(), + mainFunction->getThrownInterfaceType(), /*GenericParams=*/nullptr, ParameterList::createEmpty(context), /*FnRetType=*/TupleType::getEmpty(context), declContext); func->setSynthesized(true); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 913ae4052313..8335c7d904cd 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2192,8 +2192,8 @@ static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage, ctx, loc, /*AccessorKeywordLoc*/ loc, AccessorKind::Get, storage, staticLoc, StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), getterParams, Type(), - storage->getDeclContext()); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + getterParams, Type(), storage->getDeclContext()); getter->setSynthesized(); // If we're stealing the 'self' from a lazy initializer, set it now. @@ -2289,8 +2289,8 @@ static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage, ctx, loc, /*AccessorKeywordLoc*/ SourceLoc(), AccessorKind::Set, storage, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), params, Type(), - storage->getDeclContext()); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, Type(), storage->getDeclContext()); setter->setSynthesized(); if (isMutating) @@ -2368,7 +2368,8 @@ createCoroutineAccessorPrototype(AbstractStorageDecl *storage, ctx, loc, /*AccessorKeywordLoc=*/SourceLoc(), kind, storage, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), params, retTy, dc); + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), + params, retTy, dc); accessor->setSynthesized(); if (isMutating) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 2e7b646a2d7e..d03da955acd4 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3406,6 +3406,7 @@ class DeclDeserializer { DeclContextID contextID; bool isIUO, isFailable; bool isImplicit, isObjC, hasStubImplementation, throws, async; + TypeID thrownTypeID; GenericSignatureID genericSigID; uint8_t storedInitKind, rawAccessLevel; DeclID overriddenID; @@ -3416,7 +3417,8 @@ class DeclDeserializer { decls_block::ConstructorLayout::readRecord(scratch, contextID, isFailable, isIUO, isImplicit, isObjC, hasStubImplementation, - async, throws, storedInitKind, + async, throws, thrownTypeID, + storedInitKind, genericSigID, overriddenID, overriddenAffectsABI, @@ -3484,12 +3486,18 @@ class DeclDeserializer { if (declOrOffset.isComplete()) return declOrOffset; + auto thrownTypeOrError = MF.getTypeChecked(thrownTypeID); + if (!thrownTypeOrError) + return thrownTypeOrError.takeError(); + const auto thrownType = thrownTypeOrError.get(); + auto ctor = MF.createDecl(name, SourceLoc(), isFailable, /*FailabilityLoc=*/SourceLoc(), /*Async=*/async, /*AsyncLoc=*/SourceLoc(), /*Throws=*/throws, /*ThrowsLoc=*/SourceLoc(), + TypeLoc::withoutLoc(thrownType), /*BodyParams=*/nullptr, genericParams, parent); declOrOffset = ctor; @@ -3823,6 +3831,7 @@ class DeclDeserializer { uint8_t rawStaticSpelling, rawAccessLevel, rawMutModifier; uint8_t rawAccessorKind; bool isObjC, hasForcedStaticDispatch, async, throws; + TypeID thrownTypeID; unsigned numNameComponentsBiased; GenericSignatureID genericSigID; TypeID resultInterfaceTypeID; @@ -3841,7 +3850,7 @@ class DeclDeserializer { isStatic, rawStaticSpelling, isObjC, rawMutModifier, hasForcedStaticDispatch, - async, throws, + async, throws, thrownTypeID, genericSigID, resultInterfaceTypeID, isIUO, @@ -3859,7 +3868,7 @@ class DeclDeserializer { isStatic, rawStaticSpelling, isObjC, rawMutModifier, hasForcedStaticDispatch, - async, throws, + async, throws, thrownTypeID, genericSigID, resultInterfaceTypeID, isIUO, @@ -3976,15 +3985,20 @@ class DeclDeserializer { if (declOrOffset.isComplete()) return declOrOffset; + auto thrownTypeOrError = MF.getTypeChecked(thrownTypeID); + if (!thrownTypeOrError) + return thrownTypeOrError.takeError(); + const auto thrownType = thrownTypeOrError.get(); + FuncDecl *fn; if (!isAccessor) { fn = FuncDecl::createDeserialized(ctx, staticSpelling.value(), name, - async, throws, genericParams, - resultType, DC); + async, throws, thrownType, + genericParams, resultType, DC); } else { auto *accessor = AccessorDecl::createDeserialized( ctx, accessorKind, storage, staticSpelling.value(), async, throws, - resultType, DC); + thrownType, resultType, DC); accessor->setIsTransparent(isTransparent); fn = accessor; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index b2afac5e5ce2..7a34d73cf19f 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 808; // @_expose(wasm) +const uint16_t SWIFTMODULE_VERSION_MINOR = 809; // typed throws /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1505,6 +1505,7 @@ namespace decls_block { BCFixed<1>, // stub implementation? BCFixed<1>, // async? BCFixed<1>, // throws? + TypeIDField, // thrown error CtorInitializerKindField, // initializer kind GenericSignatureIDField, // generic environment DeclIDField, // overridden decl @@ -1579,6 +1580,7 @@ namespace decls_block { BCFixed<1>, // has forced static dispatch? BCFixed<1>, // async? BCFixed<1>, // throws? + TypeIDField, // thrown error GenericSignatureIDField, // generic environment TypeIDField, // result interface type BCFixed<1>, // IUO result? @@ -1640,6 +1642,7 @@ namespace decls_block { BCFixed<1>, // has forced static dispatch? BCFixed<1>, // async? BCFixed<1>, // throws? + TypeIDField, // thrown error GenericSignatureIDField, // generic environment TypeIDField, // result interface type BCFixed<1>, // IUO result? diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ece40f192f4b..6ed0efd370c8 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4358,6 +4358,7 @@ class Serializer::DeclSerializer : public DeclVisitor { fn->hasForcedStaticDispatch(), fn->hasAsync(), fn->hasThrows(), + S.addTypeRef(fn->getThrownInterfaceType()), S.addGenericSignatureRef( fn->getGenericSignature()), S.addTypeRef(fn->getResultInterfaceType()), @@ -4475,6 +4476,7 @@ class Serializer::DeclSerializer : public DeclVisitor { fn->hasForcedStaticDispatch(), fn->hasAsync(), fn->hasThrows(), + S.addTypeRef(fn->getThrownInterfaceType()), S.addGenericSignatureRef( fn->getGenericSignature()), S.addTypeRef(fn->getResultInterfaceType()), @@ -4640,6 +4642,7 @@ class Serializer::DeclSerializer : public DeclVisitor { ctor->hasStubImplementation(), ctor->hasAsync(), ctor->hasThrows(), + S.addTypeRef(ctor->getThrownInterfaceType()), getStableCtorInitializerKind( ctor->getInitKind()), S.addGenericSignatureRef( diff --git a/test/Parse/typed_throws.swift b/test/Parse/typed_throws.swift new file mode 100644 index 000000000000..d21ada5664d2 --- /dev/null +++ b/test/Parse/typed_throws.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test + +// Parsing support for typed throws. + +enum MyError: Error { + case epicFail +} + +func specificThrowingFunc() throws(MyError) { } +func anyThrowingFunc() throws(any Error) { } + +struct SomeStruct { + init() throws(MyError) { } +} + +typealias ThrowingFuncType = () throws(MyError) -> Void + +func testClosures() { + let _ = { () throws(MyError) in print("hello") } + let _ = { (x: Int, y: Int) throws(MyError) -> Int in x + y } +} + +func testTypes() { + let _ = [(Int, Int) throws(MyError) -> Int]() +} + +func testMissingError() throws() { } +// expected-error@-1{{expected thrown error type after 'throws('}} + +func testRethrowsWithThrownType() rethrows(MyError) { } +// expected-error@-1{{'rethrows' cannot be combined with a specific thrown error type}} diff --git a/unittests/Sema/ConstraintGenerationTests.cpp b/unittests/Sema/ConstraintGenerationTests.cpp index 384114f7a340..18d65b543d82 100644 --- a/unittests/Sema/ConstraintGenerationTests.cpp +++ b/unittests/Sema/ConstraintGenerationTests.cpp @@ -126,6 +126,7 @@ TEST_F(SemaTest, TestCaptureListIsNotOpenedEarly) { ParameterList::createEmpty(Context), /*asyncLoc=*/SourceLoc(), /*throwsLoc=*/SourceLoc(), + /*thrownType*/nullptr, /*arrowLoc=*/SourceLoc(), /*inLoc=*/SourceLoc(), /*explicitResultType=*/nullptr, DC); diff --git a/unittests/Sema/ConstraintSimplificationTests.cpp b/unittests/Sema/ConstraintSimplificationTests.cpp index e002aed57a9c..00a05e23ff64 100644 --- a/unittests/Sema/ConstraintSimplificationTests.cpp +++ b/unittests/Sema/ConstraintSimplificationTests.cpp @@ -79,6 +79,7 @@ TEST_F(SemaTest, TestClosureInferenceFromOptionalContext) { /*capturedSelfDecl=*/nullptr, ParameterList::create(Context, {paramDecl}), /*asyncLoc=*/SourceLoc(), /*throwsLoc=*/SourceLoc(), + /*thrownType=*/nullptr, /*arrowLoc=*/SourceLoc(), /*inLoc=*/SourceLoc(), /*explicitResultType=*/nullptr, From 51eed19d4b0931b68992cfd82e77980b358af3b0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Sep 2023 22:29:56 -0700 Subject: [PATCH 2/8] [Typed throws] Type system support for typed throws. Add the thrown type into the AST representation of function types, mapping from function type representations and declarations into the appropriate thrown type. Add tests for serialization, printing, and basic equivalence of function types that have thrown errors. --- include/swift/AST/Decl.h | 1 + include/swift/AST/DiagnosticsSema.def | 6 ++ include/swift/AST/ExtInfo.h | 70 +++++++++++------ include/swift/AST/TypeCheckRequests.h | 21 +++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 + include/swift/AST/Types.h | 44 +++++++---- include/swift/Basic/Features.def | 4 + lib/AST/ASTContext.cpp | 61 ++++++++++++--- lib/AST/ASTDemangler.cpp | 7 +- lib/AST/ASTDumper.cpp | 3 + lib/AST/ASTPrinter.cpp | 68 +++++++++++++---- lib/AST/Builtins.cpp | 16 ++-- lib/AST/Decl.cpp | 13 ++-- lib/AST/Type.cpp | 76 +++++++++++++++++-- lib/AST/TypeCheckRequests.cpp | 18 +++++ lib/AST/TypeSubstitution.cpp | 7 +- lib/SIL/IR/SILFunctionType.cpp | 4 +- lib/SIL/IR/TypeLowering.cpp | 9 ++- lib/SILGen/SILGenExpr.cpp | 4 +- lib/Sema/CSApply.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 12 +-- lib/Sema/TypeCheckDecl.cpp | 49 +++++++++++- lib/Sema/TypeCheckDeclObjC.cpp | 3 +- lib/Sema/TypeCheckDeclOverride.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 26 ++++++- lib/Sema/TypeCheckType.h | 2 +- lib/Serialization/Deserialization.cpp | 17 ++++- lib/Serialization/ModuleFormat.h | 2 + lib/Serialization/Serialization.cpp | 2 + test/ModuleInterface/typed_throws.swift | 13 ++++ test/Parse/typed_throws.swift | 2 +- .../Inputs/def_typed_throws.swift | 15 ++++ test/Serialization/typed_throws.swift | 22 ++++++ test/decl/func/typed_throws.swift | 40 ++++++++++ 34 files changed, 526 insertions(+), 117 deletions(-) create mode 100644 test/ModuleInterface/typed_throws.swift create mode 100644 test/Serialization/Inputs/def_typed_throws.swift create mode 100644 test/Serialization/typed_throws.swift create mode 100644 test/decl/func/typed_throws.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 5ccdf8682b2d..64927185b4e7 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6836,6 +6836,7 @@ void simple_display(llvm::raw_ostream &out, BodyAndFingerprint value); /// Base class for function-like declarations. class AbstractFunctionDecl : public GenericContext, public ValueDecl { friend class NeedsNewVTableEntryRequest; + friend class ThrownTypeRequest; public: /// records the kind of SILGen-synthesized body this decl represents diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 432ecdcc1e0f..8ae2b21d13bc 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5048,6 +5048,12 @@ WARNING(no_throw_in_try,none, WARNING(no_throw_in_do_with_catch,none, "'catch' block is unreachable because no errors are thrown in 'do' block", ()) +ERROR(experimental_typed_throws,none, + "typed throws is an experimental feature", ()) + +ERROR(thrown_type_not_error,none, + "thrown type %0 does not conform to the 'Error' protocol", (Type)) + //------------------------------------------------------------------------------ // MARK: Concurrency //------------------------------------------------------------------------------ diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 7a8a0975cfe7..fb196dbb2c46 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -370,29 +370,33 @@ class ASTExtInfoBuilder { ClangTypeInfo clangTypeInfo; Type globalActor; + Type thrownError; using Representation = FunctionTypeRepresentation; ASTExtInfoBuilder( - unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor - ) : bits(bits), clangTypeInfo(clangTypeInfo), globalActor(globalActor) {} + unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor, + Type thrownError + ) : bits(bits), clangTypeInfo(clangTypeInfo), globalActor(globalActor), + thrownError(thrownError) {} public: /// An ExtInfoBuilder for a typical Swift function: @convention(swift), /// @escaping, non-throwing, non-differentiable. ASTExtInfoBuilder() - : ASTExtInfoBuilder(Representation::Swift, false, false, + : ASTExtInfoBuilder(Representation::Swift, false, false, Type(), DifferentiabilityKind::NonDifferentiable, nullptr, Type()) {} // Constructor for polymorphic type. - ASTExtInfoBuilder(Representation rep, bool throws) - : ASTExtInfoBuilder(rep, false, throws, + ASTExtInfoBuilder(Representation rep, bool throws, Type thrownError) + : ASTExtInfoBuilder(rep, false, throws, thrownError, DifferentiabilityKind::NonDifferentiable, nullptr, Type()) {} // Constructor with no defaults. ASTExtInfoBuilder(Representation rep, bool isNoEscape, bool throws, + Type thrownError, DifferentiabilityKind diffKind, const clang::Type *type, Type globalActor) : ASTExtInfoBuilder( @@ -400,7 +404,7 @@ class ASTExtInfoBuilder { (throws ? ThrowsMask : 0) | (((unsigned)diffKind << DifferentiabilityMaskOffset) & DifferentiabilityMask), - ClangTypeInfo(type), globalActor) {} + ClangTypeInfo(type), globalActor, thrownError) {} void checkInvariants() const; @@ -438,6 +442,7 @@ class ASTExtInfoBuilder { } Type getGlobalActor() const { return globalActor; } + Type getThrownError() const { return thrownError; } constexpr bool hasSelfParam() const { switch (getSILRepresentation()) { @@ -472,31 +477,35 @@ class ASTExtInfoBuilder { return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep, shouldStoreClangType(rep) ? clangTypeInfo : ClangTypeInfo(), - globalActor); + globalActor, thrownError); } [[nodiscard]] ASTExtInfoBuilder withNoEscape(bool noEscape = true) const { return ASTExtInfoBuilder(noEscape ? (bits | NoEscapeMask) : (bits & ~NoEscapeMask), - clangTypeInfo, globalActor); + clangTypeInfo, globalActor, thrownError); } [[nodiscard]] ASTExtInfoBuilder withConcurrent(bool concurrent = true) const { return ASTExtInfoBuilder(concurrent ? (bits | SendableMask) : (bits & ~SendableMask), - clangTypeInfo, globalActor); + clangTypeInfo, globalActor, thrownError); } [[nodiscard]] ASTExtInfoBuilder withAsync(bool async = true) const { return ASTExtInfoBuilder(async ? (bits | AsyncMask) : (bits & ~AsyncMask), - clangTypeInfo, globalActor); + clangTypeInfo, globalActor, thrownError); } [[nodiscard]] - ASTExtInfoBuilder withThrows(bool throws = true) const { + ASTExtInfoBuilder withThrows(bool throws, Type thrownError) const { return ASTExtInfoBuilder( throws ? (bits | ThrowsMask) : (bits & ~ThrowsMask), clangTypeInfo, - globalActor); + globalActor, thrownError); + } + [[nodiscard]] + ASTExtInfoBuilder withThrows() const { + return withThrows(true, Type()); } [[nodiscard]] ASTExtInfoBuilder @@ -504,11 +513,12 @@ class ASTExtInfoBuilder { return ASTExtInfoBuilder( (bits & ~DifferentiabilityMask) | ((unsigned)differentiability << DifferentiabilityMaskOffset), - clangTypeInfo, globalActor); + clangTypeInfo, globalActor, thrownError); } [[nodiscard]] ASTExtInfoBuilder withClangFunctionType(const clang::Type *type) const { - return ASTExtInfoBuilder(bits, ClangTypeInfo(type), globalActor); + return ASTExtInfoBuilder( + bits, ClangTypeInfo(type), globalActor, thrownError); } /// Put a SIL representation in the ExtInfo. @@ -522,24 +532,26 @@ class ASTExtInfoBuilder { return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep, shouldStoreClangType(rep) ? clangTypeInfo : ClangTypeInfo(), - globalActor); + globalActor, thrownError); } [[nodiscard]] ASTExtInfoBuilder withGlobalActor(Type globalActor) const { - return ASTExtInfoBuilder(bits, clangTypeInfo, globalActor); + return ASTExtInfoBuilder(bits, clangTypeInfo, globalActor, thrownError); } bool isEqualTo(ASTExtInfoBuilder other, bool useClangTypes) const { return bits == other.bits && (useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) && - globalActor.getPointer() == other.globalActor.getPointer(); + globalActor.getPointer() == other.globalActor.getPointer() && + thrownError.getPointer() == thrownError.getPointer(); } - constexpr std::tuple + constexpr std::tuple getFuncAttrKey() const { return std::make_tuple( - bits, clangTypeInfo.getType(), globalActor.getPointer()); + bits, clangTypeInfo.getType(), globalActor.getPointer(), + thrownError.getPointer()); } }; // end ASTExtInfoBuilder @@ -560,8 +572,9 @@ class ASTExtInfo { // Only for use by ASTExtInfoBuilder::build. Don't use it elsewhere! ASTExtInfo(ASTExtInfoBuilder builder) : builder(builder) {} - ASTExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor) - : builder(bits, clangTypeInfo, globalActor) { + ASTExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor, + Type thrownError) + : builder(bits, clangTypeInfo, globalActor, thrownError) { builder.checkInvariants(); }; @@ -606,6 +619,7 @@ class ASTExtInfo { constexpr bool hasContext() const { return builder.hasContext(); } Type getGlobalActor() const { return builder.getGlobalActor(); } + Type getThrownError() const { return builder.getThrownError(); } /// Helper method for changing the representation. /// @@ -635,8 +649,16 @@ class ASTExtInfo { /// /// Prefer using \c ASTExtInfoBuilder::withThrows for chaining. [[nodiscard]] - ASTExtInfo withThrows(bool throws = true) const { - return builder.withThrows(throws).build(); + ASTExtInfo withThrows(bool throws, Type thrownError) const { + return builder.withThrows(throws, thrownError).build(); + } + + /// Helper method for changing only the throws field. + /// + /// Prefer using \c ASTExtInfoBuilder::withThrows for chaining. + [[nodiscard]] + ASTExtInfo withThrows() const { + return builder.withThrows(true, Type()).build(); } /// Helper method for changing only the async field. @@ -656,7 +678,7 @@ class ASTExtInfo { return builder.isEqualTo(other.builder, useClangTypes); } - constexpr std::tuple + constexpr std::tuple getFuncAttrKey() const { return builder.getFuncAttrKey(); } diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index a0df8721e362..0cf60d82bb7e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2222,6 +2222,27 @@ class ParamSpecifierRequest void cacheResult(ParamSpecifier value) const; }; +/// Determines the explicitly-written thrown result type of a function. +class ThrownTypeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + Type evaluate(Evaluator &evaluator, AbstractFunctionDecl *func) const; + +public: + // Separate caching. + bool isCached() const { return true; } + llvm::Optional getCachedResult() const; + void cacheResult(Type value) const; +}; + /// Determines the result type of a function or element type of a subscript. class ResultTypeRequest : public SimpleRequestgetCanonicalType(); - return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, - useClangFunctionType ? getCanonicalClangTypeInfo() - : ClangTypeInfo(), - globalActor); - } + ExtInfo getCanonicalExtInfo(bool useClangFunctionType) const; bool hasSameExtInfoAs(const AnyFunctionType *otherFn); @@ -3642,7 +3642,7 @@ class FunctionType final } size_t numTrailingObjects(OverloadToken) const { - return hasGlobalActor() ? 1 : 0; + return hasGlobalActor() + hasThrownError(); } public: @@ -3667,9 +3667,15 @@ class FunctionType final Type getGlobalActor() const { if (!hasGlobalActor()) return Type(); - return *getTrailingObjects(); + return getTrailingObjects()[0]; } + Type getThrownError() const { + if (!hasThrownError()) + return Type(); + return getTrailingObjects()[hasGlobalActor()]; + } + void Profile(llvm::FoldingSetNodeID &ID) { llvm::Optional info = llvm::None; if (hasExtInfo()) @@ -3774,7 +3780,7 @@ class GenericFunctionType final : public AnyFunctionType, } size_t numTrailingObjects(OverloadToken) const { - return hasGlobalActor() ? 1 : 0; + return hasGlobalActor() + hasThrownError(); } /// Construct a new generic function type. @@ -3796,9 +3802,15 @@ class GenericFunctionType final : public AnyFunctionType, Type getGlobalActor() const { if (!hasGlobalActor()) return Type(); - return *getTrailingObjects(); + return getTrailingObjects()[0]; } + Type getThrownError() const { + if (!hasThrownError()) + return Type(); + return getTrailingObjects()[hasGlobalActor()]; + } + /// Retrieve the generic signature of this function type. GenericSignature getGenericSignature() const { return Signature; diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 9867564cb9ff..5f72bda34a33 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -235,9 +235,13 @@ EXPERIMENTAL_FEATURE(RawLayout, true) /// Enables the "embedded" swift mode (no runtime). EXPERIMENTAL_FEATURE(Embedded, true) + /// Enables noncopyable generics EXPERIMENTAL_FEATURE(NoncopyableGenerics, false) +/// Enables typed throws. +EXPERIMENTAL_FEATURE(TypedThrows, true) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 223d86ba2947..8a99511b0e57 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3844,13 +3844,16 @@ DynamicSelfType *DynamicSelfType::get(Type selfType, const ASTContext &ctx) { static RecursiveTypeProperties getFunctionRecursiveProperties(ArrayRef params, - Type result, Type globalActor) { + Type result, Type globalActor, + Type thrownError) { RecursiveTypeProperties properties; for (auto param : params) properties |= param.getPlainType()->getRecursiveProperties(); properties |= result->getRecursiveProperties(); if (globalActor) properties |= globalActor->getRecursiveProperties(); + if (thrownError) + properties |= thrownError->getRecursiveProperties(); properties &= ~RecursiveTypeProperties::IsLValue; return properties; } @@ -4017,16 +4020,21 @@ void FunctionType::Profile(llvm::FoldingSetNodeID &ID, ID.AddInteger(std::get<0>(infoKey)); ID.AddPointer(std::get<1>(infoKey)); ID.AddPointer(std::get<2>(infoKey)); + ID.AddPointer(std::get<3>(infoKey)); } } FunctionType *FunctionType::get(ArrayRef params, Type result, llvm::Optional info) { + Type thrownError; Type globalActor; - if (info.has_value()) + if (info.has_value()) { + thrownError = info->getThrownError(); globalActor = info->getGlobalActor(); + } - auto properties = getFunctionRecursiveProperties(params, result, globalActor); + auto properties = getFunctionRecursiveProperties( + params, result, globalActor, thrownError); auto arena = getArena(properties); if (info.has_value()) { @@ -4058,9 +4066,10 @@ FunctionType *FunctionType::get(ArrayRef params, bool hasClangInfo = info.has_value() && !info.value().getClangTypeInfo().empty(); + unsigned numTypes = (globalActor ? 1 : 0) + (thrownError ? 1 : 0); size_t allocSize = totalSizeToAlloc< AnyFunctionType::Param, ClangTypeInfo, Type - >(params.size(), hasClangInfo ? 1 : 0, globalActor ? 1 : 0); + >(params.size(), hasClangInfo ? 1 : 0, numTypes); void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isAnyFunctionTypeCanonical(params, result); @@ -4071,6 +4080,12 @@ FunctionType *FunctionType::get(ArrayRef params, isCanonical = false; } + if (thrownError && + (!thrownError->isCanonical() || + thrownError->isNever() || + thrownError->isEqual(ctx.getErrorExistentialType()))) + isCanonical = false; + if (globalActor && !globalActor->isCanonical()) isCanonical = false; @@ -4093,8 +4108,13 @@ FunctionType::FunctionType(ArrayRef params, Type output, auto clangTypeInfo = info.value().getClangTypeInfo(); if (!clangTypeInfo.empty()) *getTrailingObjects() = clangTypeInfo; - if (Type globalActor = info->getGlobalActor()) - *getTrailingObjects() = globalActor; + unsigned thrownErrorIndex = 0; + if (Type globalActor = info->getGlobalActor()) { + getTrailingObjects()[0] = globalActor; + ++thrownErrorIndex; + } + if (Type thrownError = info->getThrownError()) + getTrailingObjects()[thrownErrorIndex] = thrownError; } } @@ -4110,6 +4130,7 @@ void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, ID.AddInteger(std::get<0>(infoKey)); ID.AddPointer(std::get<1>(infoKey)); ID.AddPointer(std::get<2>(infoKey)); + ID.AddPointer(std::get<3>(infoKey)); } } @@ -4153,15 +4174,30 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig, return funcTy; } + Type thrownError; Type globalActor; - if (info.has_value()) + if (info.has_value()) { + thrownError = info->getThrownError(); globalActor = info->getGlobalActor(); + } + + if (thrownError) { + if (!sig->isReducedType(thrownError)) { + isCanonical = false; + } else { + Type reducedThrownError = thrownError->getReducedType(sig); + if (reducedThrownError->isNever() || + reducedThrownError->isEqual(ctx.getErrorExistentialType())) + isCanonical = false; + } + } if (globalActor && !sig->isReducedType(globalActor)) isCanonical = false; + unsigned numTypes = (globalActor ? 1 : 0) + (thrownError ? 1 : 0); size_t allocSize = totalSizeToAlloc( - params.size(), globalActor ? 1 : 0); + params.size(), numTypes); void *mem = ctx.Allocate(allocSize, alignof(GenericFunctionType)); auto properties = getGenericFunctionRecursiveProperties(params, result); @@ -4183,8 +4219,13 @@ GenericFunctionType::GenericFunctionType( std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); if (info) { - if (Type globalActor = info->getGlobalActor()) - *getTrailingObjects() = globalActor; + unsigned thrownErrorIndex = 0; + if (Type globalActor = info->getGlobalActor()) { + getTrailingObjects()[0] = globalActor; + ++thrownErrorIndex; + } + if (Type thrownError = info->getThrownError()) + getTrailingObjects()[thrownErrorIndex] = thrownError; } } diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 5470a4becc8b..80659a10c960 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -454,10 +454,13 @@ Type ASTBuilder::createFunctionType( clangFunctionType = Ctx.getClangFunctionType(funcParams, output, representation); + // FIXME: Populate thrownError + Type thrownError; + auto einfo = FunctionType::ExtInfoBuilder(representation, noescape, flags.isThrowing(), - resultDiffKind, clangFunctionType, - globalActor) + thrownError, resultDiffKind, + clangFunctionType, globalActor) .withAsync(flags.isAsync()) .withConcurrent(flags.isSendable()) .build(); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 39260ac921e1..a909f215d262 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4179,6 +4179,9 @@ namespace { printFlag(T->isAsync(), "async"); printFlag(T->isThrowing(), "throws"); } + if (Type thrownError = T->getThrownError()) { + printFieldQuoted(thrownError.getString(), "thrown_error"); + } if (Type globalActor = T->getGlobalActor()) { printFieldQuoted(globalActor.getString(), "global_actor"); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 940738e9355f..fb74f11abe2e 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -958,7 +958,7 @@ class PrintAST : public ASTVisitor { void printDeclGenericRequirements(GenericContext *decl); void printPrimaryAssociatedTypes(ProtocolDecl *decl); void printBodyIfNecessary(const AbstractFunctionDecl *decl); - + void printThrownErrorIfNecessary(const AbstractFunctionDecl *decl); void printEnumElement(EnumElementDecl *elt); /// \returns true if anything was printed. @@ -2215,7 +2215,10 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) { if (asyncGet) printWithSpace("async"); - if (throwsGet) printWithSpace("throws"); + if (throwsGet) { + printWithSpace("throws"); + printThrownErrorIfNecessary(ASD->getAccessor(AccessorKind::Get)); + } if (settable) { if (nonmutatingSetter) printWithSpace("nonmutating"); @@ -2360,8 +2363,10 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) { if (accessor->getAccessorKind() == AccessorKind::Get) { if (asyncGet) printWithSpace("async"); - if (throwsGet) + if (throwsGet) { printWithSpace("throws"); + printThrownErrorIfNecessary(accessor); + } } } else { { @@ -3538,6 +3543,13 @@ static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) { return decl->hasClangNode(); } +static bool usesFeatureTypedThrows(Decl *decl) { + if (auto func = dyn_cast(decl)) + return func->getThrownTypeRepr() != nullptr; + + return false; +} + /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { @@ -4372,6 +4384,22 @@ void PrintAST::printParameterList(ParameterList *PL, Printer << ")"; } +void PrintAST::printThrownErrorIfNecessary(const AbstractFunctionDecl *AFD) { + auto thrownType = AFD->getThrownInterfaceType(); + if (!thrownType) + return; + + auto errorType = AFD->getASTContext().getErrorExistentialType(); + TypeRepr *thrownTypeRepr = AFD->getThrownTypeRepr(); + if (!errorType || !thrownType->isEqual(errorType) || + (thrownTypeRepr && Options.PreferTypeRepr)) { + Printer << "("; + TypeLoc thrownTyLoc(thrownTypeRepr, thrownType); + printTypeLoc(thrownTyLoc); + Printer << ")"; + } +} + void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { auto BodyParams = AFD->getParameters(); auto curTy = AFD->getInterfaceType(); @@ -4406,16 +4434,7 @@ void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { Printer << " " << tok::kw_rethrows; else { Printer << " " << tok::kw_throws; - - if (auto thrownType = AFD->getThrownInterfaceType()) { - auto errorType = AFD->getASTContext().getErrorExistentialType(); - TypeRepr *thrownTypeRepr = AFD->getThrownTypeRepr(); - if (!errorType || !thrownType->isEqual(errorType) || - (thrownTypeRepr && Options.PreferTypeRepr)) { - TypeLoc thrownTyLoc(thrownTypeRepr, thrownType); - printTypeLoc(thrownTyLoc); - } - } + printThrownErrorIfNecessary(AFD); } } } @@ -4481,7 +4500,10 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { // handle effects specifiers before the body if (decl->hasAsync()) Printer << " async"; - if (decl->hasThrows()) Printer << " throws"; + if (decl->hasThrows()) { + Printer << " throws"; + printThrownErrorIfNecessary(decl); + } printBodyIfNecessary(decl); } @@ -6720,8 +6742,15 @@ class TypePrinter : public TypeVisitor { Printer.printKeyword("async", Options); } - if (T->isThrowing()) + if (T->isThrowing()) { Printer << " " << tok::kw_throws; + + if (auto thrownError = T->getThrownError()) { + Printer << "("; + thrownError->print(Printer, Options); + Printer << ")"; + } + } } Printer << " -> "; @@ -6767,8 +6796,15 @@ class TypePrinter : public TypeVisitor { Printer.printKeyword("async", Options); } - if (T->isThrowing()) + if (T->isThrowing()) { Printer << " " << tok::kw_throws; + + if (auto thrownError = T->getThrownError()) { + Printer << "("; + thrownError->print(Printer, Options); + Printer << ")"; + } + } } Printer << " -> "; diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 215f5c9e9fac..aaed1cc21093 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -1267,7 +1267,7 @@ static ValueDecl *getGetObjCTypeEncodingOperation(ASTContext &Context, static ValueDecl *getAutoDiffApplyDerivativeFunction( ASTContext &Context, Identifier Id, AutoDiffDerivativeFunctionKind kind, - unsigned arity, bool throws) { + unsigned arity, bool throws, Type thrownType) { assert(arity >= 1); // JVP: // <...T...(arity), R> (@differentiable(_forward) (...T) throws -> R, ...T) @@ -1298,7 +1298,7 @@ static ValueDecl *getAutoDiffApplyDerivativeFunction( // TODO: Use `kind.getMinimalDifferentiabilityKind()`. .withDifferentiabilityKind(DifferentiabilityKind::Reverse) .withNoEscape() - .withThrows(throws) + .withThrows(throws, thrownType) .build(); SmallVector params; for (auto ¶mGen : fnParamGens) @@ -1331,7 +1331,8 @@ static ValueDecl *getAutoDiffApplyDerivativeFunction( } static ValueDecl *getAutoDiffApplyTransposeFunction( - ASTContext &Context, Identifier Id, unsigned arity, bool throws) { + ASTContext &Context, Identifier Id, unsigned arity, bool throws, + Type thrownType) { assert(arity >= 1); // <...T...(arity), R> // (@differentiable(_linear) (...T) throws -> R, ...R.TangentVector) @@ -1363,7 +1364,7 @@ static ValueDecl *getAutoDiffApplyTransposeFunction( FunctionType::ExtInfoBuilder() .withDifferentiabilityKind(DifferentiabilityKind::Linear) .withNoEscape() - .withThrows(throws) + .withThrows(throws, thrownType) .build(); SmallVector params; for (auto ¶mGen : linearFnParamGens) @@ -1903,7 +1904,7 @@ static ValueDecl *getOnceOperation(ASTContext &Context, auto ClangType = Context.getClangFunctionType(CFuncParams, VoidTy, Rep); auto Thin = FunctionType::ExtInfoBuilder(FunctionTypeRepresentation::CFunctionPointer, - /*throws*/ false) + /*throws*/ false, Type()) .withClangFunctionType(ClangType) .build(); auto BlockTy = FunctionType::get(CFuncParams, VoidTy, Thin); @@ -2551,7 +2552,7 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { OperationName, kind, arity, throws)) return nullptr; return getAutoDiffApplyDerivativeFunction(Context, Id, kind, arity, - throws); + throws, /*thrownType=*/Type()); } if (OperationName.startswith("applyTranspose_")) { unsigned arity; @@ -2559,7 +2560,8 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { if (!autodiff::getBuiltinApplyTransposeConfig( OperationName, arity, throws)) return nullptr; - return getAutoDiffApplyTransposeFunction(Context, Id, arity, throws); + return getAutoDiffApplyTransposeFunction(Context, Id, arity, throws, + /*thrownType=*/Type()); } auto BV = llvm::StringSwitch(OperationName) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a2e399094ad8..c3dafe68d4fe 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -946,13 +946,10 @@ bool Decl::preconcurrency() const { } Type AbstractFunctionDecl::getThrownInterfaceType() const { - if (!hasThrows()) - return Type(); - - // TODO: if there was an explicitly-specified thrown error type - // representation, use that for the computation. - - return getASTContext().getErrorExistentialType(); + return evaluateOrDefault( + getASTContext().evaluator, + ThrownTypeRequest{const_cast(this)}, + Type()); } Expr *AbstractFunctionDecl::getSingleExpressionBody() const { @@ -3238,7 +3235,7 @@ mapSignatureExtInfo(AnyFunctionType::ExtInfo info, .withRepresentation(info.getRepresentation()) .withConcurrent(info.isSendable()) .withAsync(info.isAsync()) - .withThrows(info.isThrowing()) + .withThrows(info.isThrowing(), info.getThrownError()) .withClangFunctionType(info.getClangTypeInfo().getType()) .build(); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 05e736315a71..f1721dfe50b7 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3321,7 +3321,7 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2, if (ext2.isThrowing() && !(ext2.isAsync() && matchMode.contains(TypeMatchFlags::AllowABICompatible))) { - ext1 = ext1.withThrows(true); + ext1 = ext1.withThrows(true, ext2.getThrownError()); } // Removing '@Sendable' is ABI-compatible because there's nothing wrong with @@ -4000,6 +4000,17 @@ ClangTypeInfo AnyFunctionType::getClangTypeInfo() const { } } +Type AnyFunctionType::getThrownError() const { + switch (getKind()) { + case TypeKind::Function: + return cast(this)->getThrownError(); + case TypeKind::GenericFunction: + return cast(this)->getThrownError(); + default: + llvm_unreachable("Illegal type kind for AnyFunctionType."); + } +} + Type AnyFunctionType::getGlobalActor() const { switch (getKind()) { case TypeKind::Function: @@ -4015,6 +4026,39 @@ ClangTypeInfo AnyFunctionType::getCanonicalClangTypeInfo() const { return getClangTypeInfo().getCanonical(); } +ASTExtInfo +AnyFunctionType::getCanonicalExtInfo(bool useClangFunctionType) const { + assert(hasExtInfo()); + Type globalActor = getGlobalActor(); + if (globalActor) + globalActor = globalActor->getCanonicalType(); + + // When there is an explicitly-specified thrown error, canonicalize it's type. + auto bits = Bits.AnyFunctionType.ExtInfoBits; + Type thrownError = getThrownError(); + if (thrownError) { + thrownError = thrownError->getCanonicalType(); + + // - If the thrown error is `any Error`, the function throws and we + // drop the thrown error. + if (thrownError->isEqual( + thrownError->getASTContext().getErrorExistentialType())) { + thrownError = Type(); + + // - If the thrown error is `Never`, the function does not throw and + // we drop the thrown error. + } else if (thrownError->isNever()) { + thrownError = Type(); + bits = bits & ~ASTExtInfoBuilder::ThrowsMask; + } + } + + return ExtInfo(bits, + useClangFunctionType ? getCanonicalClangTypeInfo() + : ClangTypeInfo(), + globalActor, thrownError); +} + bool AnyFunctionType::hasNonDerivableClangType() { auto clangTypeInfo = getClangTypeInfo(); if (clangTypeInfo.empty()) @@ -4854,6 +4898,18 @@ case TypeKind::Id: if (resultTy.getPointer() != function->getResult().getPointer()) isUnchanged = false; + // Transform the thrown error. + Type thrownError; + if (Type origThrownError = function->getThrownError()) { + thrownError = origThrownError.transformWithPosition( + TypePosition::Invariant, fn); + if (!thrownError) + return Type(); + + if (thrownError.getPointer() != origThrownError.getPointer()) + isUnchanged = false; + } + // Transform the global actor. Type globalActorType; if (Type origGlobalActorType = function->getGlobalActor()) { @@ -4883,18 +4939,22 @@ case TypeKind::Id: auto genericSig = genericFnType->getGenericSignature(); if (!function->hasExtInfo()) return GenericFunctionType::get(genericSig, substParams, resultTy); - return GenericFunctionType::get(genericSig, substParams, resultTy, - function->getExtInfo() - .withGlobalActor(globalActorType)); + return GenericFunctionType::get( + genericSig, substParams, resultTy, + function->getExtInfo() + .withGlobalActor(globalActorType) + .withThrows(function->isThrowing(), thrownError)); } if (isUnchanged) return *this; if (!function->hasExtInfo()) return FunctionType::get(substParams, resultTy); - return FunctionType::get(substParams, resultTy, - function->getExtInfo() - .withGlobalActor(globalActorType)); + return FunctionType::get( + substParams, resultTy, + function->getExtInfo() + .withGlobalActor(globalActorType) + .withThrows(function->isThrowing(), thrownError)); } case TypeKind::ArraySlice: { @@ -5355,7 +5415,7 @@ AnyFunctionType *AnyFunctionType::getWithoutDifferentiability() const { } AnyFunctionType *AnyFunctionType::getWithoutThrowing() const { - auto info = getExtInfo().intoBuilder().withThrows(false).build(); + auto info = getExtInfo().intoBuilder().withThrows(false, Type()).build(); return withExtInfo(info); } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index ff09ea2e9ba4..8b35223cb9b5 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -951,6 +951,24 @@ void ParamSpecifierRequest::cacheResult(ParamSpecifier specifier) const { decl->setSpecifier(specifier); } +//----------------------------------------------------------------------------// +// ThrownTypeRequest computation. +//----------------------------------------------------------------------------// + +llvm::Optional ThrownTypeRequest::getCachedResult() const { + auto *const func = std::get<0>(getStorage()); + Type thrownType = func->ThrownType.getType(); + if (thrownType.isNull()) + return llvm::None; + + return thrownType; +} + +void ThrownTypeRequest::cacheResult(Type type) const { + auto *const func = std::get<0>(getStorage()); + func->ThrownType.setType(type); +} + //----------------------------------------------------------------------------// // ResultTypeRequest computation. //----------------------------------------------------------------------------// diff --git a/lib/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index 9496a304dc80..5df2a8dd5328 100644 --- a/lib/AST/TypeSubstitution.cpp +++ b/lib/AST/TypeSubstitution.cpp @@ -87,8 +87,13 @@ FunctionType *GenericFunctionType::substGenericArgs( auto resultTy = substFn(getResult()); + Type thrownError = getThrownError(); + if (thrownError) + thrownError = substFn(thrownError); + // Build the resulting (non-generic) function type. - return FunctionType::get(params, resultTy, getExtInfo()); + return FunctionType::get(params, resultTy, + getExtInfo().withThrows(isThrowing(), thrownError)); } CanFunctionType diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index a5a6faf6d3b3..4f9f3f875154 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4627,13 +4627,13 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Build the uncurried function type. if (innerExtInfo.isThrowing()) - extInfo = extInfo.withThrows(true); + extInfo = extInfo.withThrows(true, innerExtInfo.getThrownError()); if (innerExtInfo.isAsync()) extInfo = extInfo.withAsync(true); // Distributed thunks are always `async throws` if (constant.isDistributedThunk()) { - extInfo = extInfo.withAsync(true).withThrows(true); + extInfo = extInfo.withAsync(true).withThrows(true, Type()); } // If this is a C++ constructor, don't add the metatype "self" parameter diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index e2d36a3a7632..0bf06114d500 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -3415,7 +3415,7 @@ static CanAnyFunctionType getDestructorInterfaceType(DestructorDecl *dd, && "There are no foreign destroying destructors"); auto extInfoBuilder = AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, - /*throws*/ false); + /*throws*/ false, Type()); if (isForeign) extInfoBuilder = extInfoBuilder.withSILRepresentation( SILFunctionTypeRepresentation::ObjCMethod); @@ -3452,7 +3452,7 @@ static CanAnyFunctionType getIVarInitDestroyerInterfaceType(ClassDecl *cd, : classType); auto extInfoBuilder = AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, - /*throws*/ false); + /*throws*/ false, Type()); auto extInfo = extInfoBuilder .withSILRepresentation( isObjC ? SILFunctionTypeRepresentation::ObjCMethod @@ -3481,7 +3481,8 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, auto innerExtInfo = AnyFunctionType::ExtInfoBuilder(FunctionType::Representation::Thin, - funcType->isThrowing()) + funcType->isThrowing(), + funcType->getThrownError()) .withConcurrent(funcType->isSendable()) .withAsync(funcType->isAsync()) .build(); @@ -3513,7 +3514,7 @@ static CanAnyFunctionType getAsyncEntryPoint(ASTContext &C) { CanType returnType = C.getVoidType()->getCanonicalType(); FunctionType::ExtInfo extInfo = - FunctionType::ExtInfoBuilder().withAsync(true).withThrows(false).build(); + FunctionType::ExtInfoBuilder().withAsync(true).build(); return CanAnyFunctionType::get(/*genericSig*/ nullptr, {}, returnType, extInfo); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 96df23508ad9..373b9009518c 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1861,12 +1861,12 @@ static bool canPeepholeLiteralClosureConversion(Type literalType, // TODO: We could also in principle let `async` through here, but that // interferes with the implementation of `reasync`. auto literalWithoutEffects = literalFnType->getExtInfo().intoBuilder() - .withThrows(false) + .withThrows(false, Type()) .withNoEscape(false) .build(); auto convertedWithoutEffects = convertedFnType->getExtInfo().intoBuilder() - .withThrows(false) + .withThrows(false, Type()) .withNoEscape(false) .build(); if (literalFnType->withExtInfo(literalWithoutEffects) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 61b2c0eae84d..74bdf7cdf984 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8896,7 +8896,7 @@ static Expr *wrapAsyncLetInitializer( auto extInfo = ASTExtInfoBuilder() .withAsync() .withConcurrent() - .withThrows(throws) + .withThrows(throws, /*FIXME:*/Type()) .build(); // Form the autoclosure expression. The actual closure here encapsulates the diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 595cf3de9709..89ff7ce0d461 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2920,7 +2920,7 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( FunctionType::ExtInfoBuilder() .withNoEscape(true) .withAsync(true) - .withThrows(true) + .withThrows(true, /*FIXME:*/Type()) .build()); FunctionType::Param args[] = { FunctionType::Param(noescapeClosure), @@ -2931,7 +2931,7 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( FunctionType::ExtInfoBuilder() .withNoEscape(false) .withAsync(true) - .withThrows(true) + .withThrows(true, /*FIXME:*/Type()) .build()); return {refType, refType, refType, refType}; } @@ -2953,7 +2953,7 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( auto bodyClosure = FunctionType::get(bodyArgs, result, FunctionType::ExtInfoBuilder() .withNoEscape(true) - .withThrows(true) + .withThrows(true, /*FIXME:*/Type()) .withAsync(true) .build()); FunctionType::Param args[] = { @@ -2963,7 +2963,7 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( auto refType = FunctionType::get(args, result, FunctionType::ExtInfoBuilder() .withNoEscape(false) - .withThrows(true) + .withThrows(true, /*FIXME:*/Type()) .withAsync(true) .build()); return {refType, refType, refType, refType}; @@ -3227,7 +3227,7 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate( bool concurrent = expr->getAttrs().hasAttribute(); if (throws || async) { return ASTExtInfoBuilder() - .withThrows(throws) + .withThrows(throws, /*FIXME:*/Type()) .withAsync(async) .withConcurrent(concurrent) .build(); @@ -3241,7 +3241,7 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate( auto throwFinder = FindInnerThrows(expr); body->walk(throwFinder); return ASTExtInfoBuilder() - .withThrows(throwFinder.foundThrow()) + .withThrows(throwFinder.foundThrow(), /*FIXME:*/Type()) .withAsync(bool(findAsyncNode(expr))) .withConcurrent(concurrent) .build(); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c06b6119640d..3c609b1ced17 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2084,6 +2084,34 @@ static Type buildAddressorResultType(AccessorDecl *addressor, return valueType->wrapInPointer(pointerKind); } +Type +ThrownTypeRequest::evaluate( + Evaluator &evaluator, AbstractFunctionDecl *func +) const { + ASTContext &ctx = func->getASTContext(); + + TypeRepr *typeRepr = func->getThrownTypeRepr(); + if (!typeRepr) { + // There is no explicit thrown type, so return a NULL type. + return Type(); + } + + if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) { + ctx.Diags.diagnose(typeRepr->getLoc(), diag::experimental_typed_throws); + } + + auto options = TypeResolutionOptions(TypeResolverContext::None); + if (func->preconcurrency()) + options |= TypeResolutionFlags::Preconcurrency; + + auto *const dc = func->getInnermostDeclContext(); + return TypeResolution::forInterface(dc, options, + /*unboundTyOpener*/ nullptr, + PlaceholderType::get, + /*packElementOpener*/ nullptr) + .resolveType(typeRepr); +} + Type ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { auto &ctx = decl->getASTContext(); @@ -2486,6 +2514,25 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { AnyFunctionType::ExtInfoBuilder infoBuilder; + // Thrown error type. + Type thrownTy = AFD->getThrownInterfaceType(); + if (thrownTy) { + thrownTy = AFD->getThrownInterfaceType(); + ProtocolDecl *errorProto = Context.getErrorDecl(); + if (thrownTy && errorProto) { + Type thrownTyInContext = AFD->mapTypeIntoContext(thrownTy); + if (!TypeChecker::conformsToProtocol( + thrownTyInContext, errorProto, AFD->getParentModule())) { + SourceLoc loc; + if (auto thrownTypeRepr = AFD->getThrownTypeRepr()) + loc = thrownTypeRepr->getLoc(); + else + loc = AFD->getLoc(); + Context.Diags.diagnose(loc, diag::thrown_type_not_error, thrownTy); + } + } + } + // Result Type resultTy; if (auto fn = dyn_cast(D)) { @@ -2507,7 +2554,7 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { infoBuilder = infoBuilder.withAsync(AFD->hasAsync()); infoBuilder = infoBuilder.withConcurrent(AFD->isSendable()); // 'throws' only applies to the innermost function. - infoBuilder = infoBuilder.withThrows(AFD->hasThrows()); + infoBuilder = infoBuilder.withThrows(AFD->hasThrows(), thrownTy); // Defer bodies must not escape. if (auto fd = dyn_cast(D)) infoBuilder = infoBuilder.withNoEscape(fd->isDeferBody()); diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index b1fae03d75b0..b8b3f8ac6466 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -844,7 +844,8 @@ bool swift::isRepresentableInObjC( Type completionHandlerType = FunctionType::get( completionHandlerParams, TupleType::getEmpty(ctx), - ASTExtInfoBuilder(FunctionTypeRepresentation::Block, false).build()); + ASTExtInfoBuilder(FunctionTypeRepresentation::Block, false, Type()) + .build()); asyncConvention = ForeignAsyncConvention( completionHandlerType->getCanonicalType(), completionHandlerParamIndex, diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 9aa62497aecb..3da9bce843d7 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -35,7 +35,7 @@ static void adjustFunctionTypeForOverride(Type &type) { // with one returning Never? auto fnType = type->castTo(); auto extInfo = fnType->getExtInfo(); - extInfo = extInfo.withThrows(false); + extInfo = extInfo.withThrows(false, Type()); if (!fnType->getExtInfo().isEqualTo(extInfo, useClangTypes(fnType))) type = fnType->withExtInfo(extInfo); } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index dc58ab8afc4e..b2a74c0c4196 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3620,9 +3620,31 @@ NeverNullType TypeResolver::resolveASTFunctionType( } } + Type thrownTy; + if (auto thrownTypeRepr = repr->getThrownTypeRepr()) { + ASTContext &ctx = getASTContext(); + if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) { + diagnoseInvalid( + thrownTypeRepr, thrownTypeRepr->getLoc(), diag::experimental_typed_throws); + } + + auto thrownTypeOptions = options.withoutContext(); + thrownTy = resolveType(thrownTypeRepr, thrownTypeOptions); + if (thrownTy->hasError()) { + thrownTy = Type(); + } else if (!options.contains(TypeResolutionFlags::SilenceErrors) && + !TypeChecker::conformsToProtocol( + thrownTy, ctx.getErrorDecl(), + resolution.getDeclContext()->getParentModule())) { + diagnoseInvalid( + thrownTypeRepr, thrownTypeRepr->getLoc(), diag::thrown_type_not_error, + thrownTy); + } + } + FunctionType::ExtInfoBuilder extInfoBuilder( - FunctionTypeRepresentation::Swift, noescape, repr->isThrowing(), diffKind, - /*clangFunctionType*/ nullptr, Type()); + FunctionTypeRepresentation::Swift, noescape, repr->isThrowing(), thrownTy, + diffKind, /*clangFunctionType*/ nullptr, Type()); const clang::Type *clangFnType = parsedClangFunctionType; if (shouldStoreClangType(representation) && !clangFnType) diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index 0a4f8362eefd..0111f18cee30 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -192,7 +192,7 @@ enum class TypeResolverContext : uint8_t { AssociatedTypeInherited, /// Whether this is a custom attribute. - CustomAttr + CustomAttr, }; /// Options that determine how type resolution should work. diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index d03da955acd4..76015325ac4a 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6478,6 +6478,7 @@ detail::function_deserializer::deserialize(ModuleFile &MF, TypeID resultID; uint8_t rawRepresentation, rawDiffKind; bool noescape = false, concurrent, async, throws; + TypeID thrownErrorID; GenericSignature genericSig; TypeID clangTypeID; TypeID globalActorTypeID; @@ -6485,12 +6486,12 @@ detail::function_deserializer::deserialize(ModuleFile &MF, if (!isGeneric) { decls_block::FunctionTypeLayout::readRecord( scratch, resultID, rawRepresentation, clangTypeID, noescape, concurrent, - async, throws, rawDiffKind, globalActorTypeID); + async, throws, thrownErrorID, rawDiffKind, globalActorTypeID); } else { GenericSignatureID rawGenericSig; decls_block::GenericFunctionTypeLayout::readRecord( scratch, resultID, rawRepresentation, concurrent, async, throws, - rawDiffKind, globalActorTypeID, rawGenericSig); + thrownErrorID, rawDiffKind, globalActorTypeID, rawGenericSig); genericSig = MF.getGenericSignature(rawGenericSig); clangTypeID = 0; } @@ -6499,6 +6500,15 @@ detail::function_deserializer::deserialize(ModuleFile &MF, if (!representation.has_value()) return MF.diagnoseFatal(); + Type thrownError; + if (thrownErrorID) { + auto thrownErrorTy = MF.getTypeChecked(thrownErrorID); + if (!thrownErrorTy) + return thrownErrorTy.takeError(); + + thrownError = thrownErrorTy.get(); + } + auto diffKind = getActualDifferentiabilityKind(rawDiffKind); if (!diffKind.has_value()) return MF.diagnoseFatal(); @@ -6521,7 +6531,8 @@ detail::function_deserializer::deserialize(ModuleFile &MF, } auto info = - FunctionType::ExtInfoBuilder(*representation, noescape, throws, *diffKind, + FunctionType::ExtInfoBuilder(*representation, noescape, throws, + thrownError, *diffKind, clangFunctionType, globalActor) .withConcurrent(concurrent) .withAsync(async) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 7a34d73cf19f..006913ddf5e8 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1193,6 +1193,7 @@ namespace decls_block { BCFixed<1>, // concurrent? BCFixed<1>, // async? BCFixed<1>, // throws? + TypeIDField, // thrown error DifferentiabilityKindField, // differentiability kind TypeIDField // global actor // trailed by parameters @@ -1286,6 +1287,7 @@ namespace decls_block { BCFixed<1>, // concurrent? BCFixed<1>, // async? BCFixed<1>, // throws? + TypeIDField, // thrown error DifferentiabilityKindField, // differentiability kind TypeIDField, // global actor GenericSignatureIDField // generic signature diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 6ed0efd370c8..fc4393b45d50 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -5344,6 +5344,7 @@ class Serializer::TypeSerializer : public TypeVisitor { fnTy->isSendable(), fnTy->isAsync(), fnTy->isThrowing(), + S.addTypeRef(fnTy->getThrownError()), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), globalActor); @@ -5359,6 +5360,7 @@ class Serializer::TypeSerializer : public TypeVisitor { S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->isSendable(), fnTy->isAsync(), fnTy->isThrowing(), + S.addTypeRef(fnTy->getThrownError()), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), S.addTypeRef(fnTy->getGlobalActor()), S.addGenericSignatureRef(genericSig)); diff --git a/test/ModuleInterface/typed_throws.swift b/test/ModuleInterface/typed_throws.swift new file mode 100644 index 000000000000..ea2831fe4c6e --- /dev/null +++ b/test/ModuleInterface/typed_throws.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature TypedThrows +// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test -enable-experimental-feature TypedThrows +// RUN: %FileCheck %s < %t.swiftinterface + +public enum MyError: Error { + case fail +} + +// CHECK: #if compiler(>=5.3) && $TypedThrows +// CHECK-NEXT: public func throwsMyError() throws(Test.MyError) +// CHECK-NEXT: #endif +public func throwsMyError() throws(MyError) { } diff --git a/test/Parse/typed_throws.swift b/test/Parse/typed_throws.swift index d21ada5664d2..84e95f53a749 100644 --- a/test/Parse/typed_throws.swift +++ b/test/Parse/typed_throws.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test -enable-experimental-feature TypedThrows // Parsing support for typed throws. diff --git a/test/Serialization/Inputs/def_typed_throws.swift b/test/Serialization/Inputs/def_typed_throws.swift new file mode 100644 index 000000000000..89ba6ea499a7 --- /dev/null +++ b/test/Serialization/Inputs/def_typed_throws.swift @@ -0,0 +1,15 @@ +public enum MyError: Error { + case fail +} + +public func throwsMyError() throws(MyError) { } + +public struct SomeStruct { + public init() throws(MyError) { } + + public var value: Int { + get throws(MyError) { + return 17 + } + } +} diff --git a/test/Serialization/typed_throws.swift b/test/Serialization/typed_throws.swift new file mode 100644 index 000000000000..2e94f81321fa --- /dev/null +++ b/test/Serialization/typed_throws.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -module-name def_typed_throws -o %t %S/Inputs/def_typed_throws.swift -enable-experimental-feature TypedThrows +// RUN: llvm-bcanalyzer %t/def_typed_throws.swiftmodule | %FileCheck %s +// RUN: %target-typecheck-verify-swift -I %t + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -module-to-print=def_typed_throws -I %t -source-filename=%s | %FileCheck -check-prefix=CHECK-PRINT %s + +// CHECK-NOT: UnknownCode + +// CHECK-PRINT-DAG: func throwsMyError() throws(MyError) +// CHECK-PRINT-DAG: init() throws(MyError) +// CHECK-PRINT-DAG: var value: Int { get throws(MyError) } + +import def_typed_throws + +func testThrows() { + let _: () -> Void = throwsMyError + // expected-error@-1{{invalid conversion from throwing function of type '() throws(MyError) -> ()'}} + + let _: () -> Void = SomeStruct.init + // expected-error@-1{{invalid conversion from throwing function of type '() throws(MyError) -> SomeStruct'}} +} diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift new file mode 100644 index 000000000000..f5f4dc24c65a --- /dev/null +++ b/test/decl/func/typed_throws.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test -enable-experimental-feature TypedThrows + +enum MyError: Error { + case fail +} + +enum MyBadError { + case epicFail +} + +// Thrown types must conform to Error +func testBadThrownError() throws(MyBadError) { } +// expected-error@-1{{thrown type 'MyBadError' does not conform to the 'Error' protocol}} + +typealias BadThrowsMyError = () throws(MyBadError) -> Void +// expected-error@-1{{thrown type 'MyBadError' does not conform to the 'Error' protocol}} + +func hasThrownMyError() throws(MyError) { } + +typealias ThrowsMyError = () throws(MyError) -> Void + +func testThrownMyErrorType() { + let _: ThrowsMyError = hasThrownMyError + let _: () -> Void = hasThrownMyError + // expected-error@-1{{invalid conversion from throwing function of type '() throws(MyError) -> ()' to non-throwing function type '() -> Void'}} +} + +func throwsGeneric(errorType: T.Type) throws(T) { } + +func throwsBadGeneric(errorType: T.Type) throws(T) { } +// expected-error@-1{{thrown type 'T' does not conform to the 'Error' protocol}} + +func throwsUnusedInSignature() throws(T) { } +// expected-error@-1{{generic parameter 'T' is not used in function signature}} + + +func testSubstitutedType() { + let _: (_: MyError.Type) -> Void = throwsGeneric + // expected-error@-1{{invalid conversion from throwing function of type '(MyError.Type) throws(MyError) -> ()'}} +} From 54589e12f52142674cb4a8070b7e7bd7c9743e70 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Sep 2023 23:15:18 -0700 Subject: [PATCH 3/8] [Typed throws] Type-check thrown expressions against the thrown error type Rather than always type-check the expression in a `throw` statement for conformance to `Error`, check that it converts to the thrown error type. --- include/swift/AST/AnyFunctionRef.h | 18 ++++++++++++++++++ include/swift/AST/DiagnosticsSema.def | 4 +++- lib/Sema/CSDiagnostics.cpp | 19 +++++++++++++++---- lib/Sema/TypeCheckStmt.cpp | 14 ++++++++++++-- test/decl/func/typed_throws.swift | 22 ++++++++++++++++++++++ 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index ce2510a6194c..b22b3c88d86e 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -124,6 +124,24 @@ class AnyFunctionRef { return TheFunction.get()->getType(); } + Type getThrownErrorType() const { + if (auto *AFD = TheFunction.dyn_cast()) { + if (Type thrownError = AFD->getThrownInterfaceType()) + return AFD->mapTypeIntoContext(thrownError); + + return Type(); + } + + Type closureType = TheFunction.get()->getType(); + if (!closureType) + return Type(); + + if (auto closureFnType = closureType->getAs()) + return closureFnType->getThrownError(); + + return Type(); + } + Type getBodyResultType() const { if (auto *AFD = TheFunction.dyn_cast()) { if (auto *FD = dyn_cast(AFD)) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8ae2b21d13bc..d9bfcda692d0 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -336,7 +336,9 @@ ERROR(cannot_convert_to_return_type_nil,none, "'nil' is incompatible with return type %0", (Type)) ERROR(cannot_convert_thrown_type,none, - "thrown expression type %0 does not conform to 'Error'", (Type)) + "thrown expression type %0 %select{cannot be converted to error type %1|" + "does not conform to 'Error'}2", + (Type, Type, bool)) ERROR(cannot_throw_error_code,none, "thrown error code type %0 does not conform to 'Error'; construct an %1 " "instance", (Type, Type)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 0f4a947a3b22..d25fe6fc107a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3136,15 +3136,24 @@ bool ContextualFailure::diagnoseThrowsTypeMismatch() const { auto anchor = getAnchor(); + auto &Ctx = getASTContext(); + Type toType = getToType(); + bool toErrorExistential = false; + if (toType->isEqual(Ctx.getErrorExistentialType())) + toErrorExistential = true; + else if (auto protoType = toType->getAs()) { + toErrorExistential = protoType->getDecl()->isSpecificProtocol( + KnownProtocolKind::Error); + } + // If we tried to throw the error code of an error type, suggest object // construction. - auto &Ctx = getASTContext(); if (auto errorCodeProtocol = Ctx.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) { Type errorCodeType = getFromType(); auto conformance = TypeChecker::conformsToProtocol( errorCodeType, errorCodeProtocol, getParentModule()); - if (conformance) { + if (conformance && toErrorExistential) { Type errorType = conformance .getTypeWitnessByName(errorCodeType, getASTContext().Id_ErrorType) @@ -3164,8 +3173,10 @@ bool ContextualFailure::diagnoseThrowsTypeMismatch() const { // The conversion destination of throw is always ErrorType (at the moment) // if this ever expands, this should be a specific form like () is for // return. - emitDiagnostic(diag::cannot_convert_thrown_type, getFromType()) - .highlight(getSourceRange()); + emitDiagnostic( + diag::cannot_convert_thrown_type, getFromType(), toType, + toErrorExistential) + .highlight(getSourceRange()); return true; } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d676a6ff3fa0..c515600d371c 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1195,10 +1195,20 @@ class StmtChecker : public StmtVisitor { // Coerce the operand to the exception type. auto E = TS->getSubExpr(); - if (!getASTContext().getErrorDecl()) + + + Type errorType; + if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) { + errorType = TheFunc->getThrownErrorType(); + } + + if (!errorType) { + errorType = getASTContext().getErrorExistentialType(); + } + + if (!errorType) return TS; - Type errorType = getASTContext().getErrorExistentialType(); TypeChecker::typeCheckExpression(E, DC, {errorType, CTP_ThrowStmt}); TS->setSubExpr(E); diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index f5f4dc24c65a..d8f7bf7f44e6 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -4,6 +4,10 @@ enum MyError: Error { case fail } +enum MyOtherError: Error { + case fail +} + enum MyBadError { case epicFail } @@ -37,4 +41,22 @@ func throwsUnusedInSignature() throws(T) { } func testSubstitutedType() { let _: (_: MyError.Type) -> Void = throwsGeneric // expected-error@-1{{invalid conversion from throwing function of type '(MyError.Type) throws(MyError) -> ()'}} + + let _: (_: (any Error).Type) -> Void = throwsGeneric + // expected-error@-1{{invalid conversion from throwing function of type '((any Error).Type) throws((any Error)) -> ()' to non-throwing function type}} + + let _: (_: Never.Type) -> Void = throwsGeneric + // FIXME wrong: expected-error@-1{{invalid conversion from throwing function of type '(Never.Type) throws(Never) -> ()'}} +} + + +func testThrowingInFunction(cond: Bool, cond2: Bool) throws(MyError) { + if cond { + throw MyError.fail + } else if cond2 { + throw .fail + } else { + throw MyBadError.epicFail + // expected-error@-1{{thrown expression type 'MyBadError' cannot be converted to error type 'MyError'}} + } } From 7f82b2a9aae7a658a13c8b957f460e86cd6101f7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Sep 2023 19:57:50 -0700 Subject: [PATCH 4/8] [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. --- .../swift/Sema/ConstraintLocatorPathElts.def | 3 ++ lib/Sema/CSGen.cpp | 43 +++++++++++++++++++ lib/Sema/CSSyntacticElement.cpp | 31 +++++++++++-- lib/Sema/ConstraintLocator.cpp | 5 +++ lib/Sema/ConstraintSystem.cpp | 10 +++++ lib/Sema/TypeCheckStmt.cpp | 2 - test/expr/closure/typed_throws.swift | 35 +++++++++++++++ 7 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 test/expr/closure/typed_throws.swift diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 82e6b3ab922f..feee1045599b 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -54,6 +54,9 @@ SIMPLE_LOCATOR_PATH_ELT(AutoclosureResult) /// The result of a closure. SIMPLE_LOCATOR_PATH_ELT(ClosureResult) +/// The thrown error of a closure. +SIMPLE_LOCATOR_PATH_ELT(ClosureThrownError) + /// FIXME: Misleading name: this locator is used only for single-expression /// closure returns. /// diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 250ea5dcc893..fc533690857c 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2453,6 +2453,49 @@ namespace { auto resultLocator = CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); + // FIXME: Need a better locator. + auto thrownErrorLocator = + CS.getConstraintLocator(closure, ConstraintLocator::ClosureThrownError); + + // Determine the thrown error type, when appropriate. + Type thrownErrorTy = [&] { + // Explicitly-specified thrown type. + if (auto thrownTypeRepr = closure->getExplicitThrownTypeRepr()) { + Type resolvedTy = resolveTypeReferenceInExpression( + thrownTypeRepr, TypeResolverContext::InExpression, + thrownErrorLocator); + if (resolvedTy) + return resolvedTy; + } + + // Thrown type inferred from context. + if (auto contextualType = CS.getContextualType( + closure, /*forConstraint=*/false)) { + if (auto fnType = contextualType->getAs()) { + if (Type thrownErrorTy = fnType->getThrownError()) + return thrownErrorTy; + } + } + + // We do not try to infer a thrown error type if one isn't immediately + // available. We could attempt this in the future. + return Type(); + }(); + + if (thrownErrorTy) { + // Record the thrown error type in the extended info for the function + // type of the closure. + extInfo = extInfo.withThrows(true, thrownErrorTy); + + // Ensure that the thrown error type conforms to Error. + if (auto errorProto = + CS.getASTContext().getProtocol(KnownProtocolKind::Error)) { + CS.addConstraint( + ConstraintKind::ConformsTo, thrownErrorTy, + errorProto->getDeclaredInterfaceType(), thrownErrorLocator); + } + } + // Closure expressions always have function type. In cases where a // parameter or return type is omitted, a fresh type variable is used to // stand in for that parameter or return type, allowing it to be inferred diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 9ef4c791ec10..b8798901a175 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -908,12 +908,18 @@ class SyntacticElementConstraintGenerator } void visitThrowStmt(ThrowStmt *throwStmt) { - if (!cs.getASTContext().getErrorDecl()) { - hadError = true; - return; + + // Find the thrown type of our current context. + Type errType = getContextualThrownErrorType(); + if (!errType) { + if (!cs.getASTContext().getErrorDecl()) { + hadError = true; + return; + } + + errType = cs.getASTContext().getErrorExistentialType(); } - auto errType = cs.getASTContext().getErrorExistentialType(); auto *errorExpr = throwStmt->getSubExpr(); createConjunction( @@ -1300,6 +1306,18 @@ class SyntacticElementConstraintGenerator return {funcRef->getBodyResultType(), CTP_ReturnStmt}; } + Type getContextualThrownErrorType() const { + auto funcRef = AnyFunctionRef::fromDeclContext(context.getAsDeclContext()); + if (!funcRef) + return Type(); + + if (auto *closure = + getAsExpr(funcRef->getAbstractClosureExpr())) + return cs.getClosureType(closure)->getThrownError(); + + return funcRef->getThrownErrorType(); + } + #define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \ llvm_unreachable("Unsupported statement kind " #STMT); \ } @@ -2520,6 +2538,11 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( if (llvm::is_contained(solution.preconcurrencyClosures, closure)) closure->setIsolatedByPreconcurrency(); + // Coerce the thrown type, if it was written explicitly. + if (closure->getExplicitThrownType()) { + closure->setExplicitThrownType(closureFnType->getThrownError()); + } + // Coerce the result type, if it was written explicitly. if (closure->hasExplicitResultType()) { closure->setExplicitResultType(closureFnType->getResult()); diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 8a8d3f95f237..6af5e6479255 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -45,6 +45,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ApplyFunction: case ConstraintLocator::SequenceElementType: case ConstraintLocator::ClosureResult: + case ConstraintLocator::ClosureThrownError: case ConstraintLocator::ClosureBody: case ConstraintLocator::ConstructorMember: case ConstraintLocator::ConstructorMemberType: @@ -187,6 +188,10 @@ void LocatorPathElt::dump(raw_ostream &out) const { out << "closure result"; break; + case ConstraintLocator::ClosureThrownError: + out << "closure thrown error"; + break; + case ConstraintLocator::ClosureBody: out << "type of a closure body"; break; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 89ff7ce0d461..f8ec5c52b6ed 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5809,6 +5809,16 @@ void constraints::simplifyLocator(ASTNode &anchor, } break; + case ConstraintLocator::ClosureThrownError: + if (auto CE = getAsExpr(anchor)) { + if (auto thrownTypeRepr = CE->getExplicitThrownTypeRepr()) { + anchor = thrownTypeRepr; + path = path.slice(1); + break; + } + } + break; + case ConstraintLocator::CoercionOperand: { auto *CE = castToExpr(anchor); anchor = CE->getSubExpr()->getValueProvidingExpr(); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c515600d371c..154a25dc3bdc 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1195,8 +1195,6 @@ class StmtChecker : public StmtVisitor { // Coerce the operand to the exception type. auto E = TS->getSubExpr(); - - Type errorType; if (auto TheFunc = AnyFunctionRef::fromDeclContext(DC)) { errorType = TheFunc->getThrownErrorType(); diff --git a/test/expr/closure/typed_throws.swift b/test/expr/closure/typed_throws.swift new file mode 100644 index 000000000000..58478c3afdba --- /dev/null +++ b/test/expr/closure/typed_throws.swift @@ -0,0 +1,35 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows + +enum MyError: Error { + case fail +} + +enum MyBadError { + case fail +} + +func testClosures() { + let c1 = { () throws(MyError) in + throw .fail + } + + let _: () -> Void = c1 + // expected-error@-1{{invalid conversion from throwing function of type '() throws(MyError) -> ()'}} + + let _: () throws(MyError) -> Void = { + throw .fail + } + + // FIXME: Terrible diagnostic. + // expected-error@+1{{unable to infer closure type without a type annotation}} + let _ = { () throws(MyBadError) in + throw MyBadError.fail + } + + // We do not infer thrown error types from the body, because doing so would + // break existing code. + let c2 = { throw MyError.fail } + let _: Int = c2 + // expected-error@-1{{cannot convert value of type '() throws -> ()'}} +} + From 31b8811167c43d2c093127f01d0321ab8a1c5233 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Sep 2023 23:01:33 -0700 Subject: [PATCH 5/8] [Typed throws] Perform access control and availability checking on thrown errors --- include/swift/AST/DiagnosticsSema.def | 8 ++--- lib/Sema/TypeCheckAccess.cpp | 36 +++++++++++++++++-- test/attr/typed_throws_availability_osx.swift | 15 ++++++++ test/decl/func/typed_throws.swift | 4 +++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 test/attr/typed_throws_availability_osx.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d9bfcda692d0..1de62bb5b1ee 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2267,10 +2267,10 @@ ERROR(function_type_access,none, "%select{private|fileprivate|internal|package|%error|%error}1|private or fileprivate}2" "|cannot be declared " "%select{in this context|fileprivate|internal|package|public|open}1}0 " - "because its %select{parameter|result}5 uses " + "because its %select{parameter|thrown error|result}5 uses " "%select{a private|a fileprivate|an internal|a package|an '@_spi'|an '@_spi'}3" "%select{| API wrapper}6 type", - (bool, AccessLevel, bool, AccessLevel, unsigned, bool, bool)) + (bool, AccessLevel, bool, AccessLevel, unsigned, unsigned, bool)) ERROR(function_type_spi,none, "%select{function|method|initializer}0 " "cannot be declared '@_spi' " @@ -2282,10 +2282,10 @@ WARNING(function_type_access_warn,none, "%select{function|method|initializer}4 " "%select{should be declared %select{private|fileprivate|internal|package|%error|%error}1" "|should not be declared %select{in this context|fileprivate|internal|package|public|open}1}0 " - "because its %select{parameter|result}5 uses " + "because its %select{parameter|thrown error|result}5 uses " "%select{a private|a fileprivate|an internal|a package|%error|%error}3 " "%select{|API wrapper}6 type", - (bool, AccessLevel, bool, AccessLevel, unsigned, bool, bool)) + (bool, AccessLevel, bool, AccessLevel, unsigned, unsigned, bool)) ERROR(function_type_usable_from_inline,none, "the %select{parameter|result}1%select{| API wrapper}2 of a " "'@usableFromInline' %select{function|method|initializer}0 " diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index f3c9c152f12c..caad0f1e71b2 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1040,10 +1040,18 @@ class AccessControlChecker : public AccessControlCheckerBase, FK_Initializer }; + // This must stay in sync with diag::function_type_access + enum { + EK_Parameter = 0, + EK_ThrownError, + EK_Result + }; + auto minAccessScope = AccessScope::getPublic(); const TypeRepr *complainRepr = nullptr; auto downgradeToWarning = DowngradeToWarning::No; ImportAccessLevel minImportLimit = llvm::None; + unsigned entityKind = EK_Parameter; bool hasInaccessibleParameterWrapper = false; for (auto *P : *fn->getParameters()) { @@ -1085,7 +1093,25 @@ class AccessControlChecker : public AccessControlCheckerBase, }); } - bool problemIsResult = false; + if (auto thrownTypeRepr = fn->getThrownTypeRepr()) { + checkTypeAccess( + fn->getThrownInterfaceType(), thrownTypeRepr, fn, + /*mayBeInferred*/ false, + [&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr, + DowngradeToWarning downgradeDiag, ImportAccessLevel importLimit) { + if (typeAccessScope.isChildOf(minAccessScope) || + (!complainRepr && + typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { + minAccessScope = typeAccessScope; + complainRepr = thisComplainRepr; + downgradeToWarning = downgradeDiag; + minImportLimit = importLimit; + entityKind = EK_ThrownError; + } + }); + } + + if (auto FD = dyn_cast(fn)) { checkTypeAccess(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD, /*mayBeInferred*/false, @@ -1100,7 +1126,7 @@ class AccessControlChecker : public AccessControlCheckerBase, complainRepr = thisComplainRepr; downgradeToWarning = downgradeDiag; minImportLimit = importLimit; - problemIsResult = true; + entityKind = EK_Result; } }); } @@ -1122,7 +1148,7 @@ class AccessControlChecker : public AccessControlCheckerBase, diagID = diag::function_type_access_warn; auto diag = fn->diagnose(diagID, isExplicit, fnAccess, isa(fn->getDeclContext()), minAccess, - functionKind, problemIsResult, + functionKind, entityKind, hasInaccessibleParameterWrapper); highlightOffendingType(diag, complainRepr); noteLimitingImport(fn->getASTContext(), minImportLimit, complainRepr); @@ -2181,6 +2207,10 @@ class DeclAvailabilityChecker : public DeclVisitor { checkType(P->getInterfaceType(), P->getTypeRepr(), fn); } + + if (auto thrownTypeRepr = fn->getThrownTypeRepr()) { + checkType(fn->getThrownInterfaceType(), thrownTypeRepr, fn); + } } void visitFuncDecl(FuncDecl *FD) { diff --git a/test/attr/typed_throws_availability_osx.swift b/test/attr/typed_throws_availability_osx.swift new file mode 100644 index 000000000000..7edc67eecd02 --- /dev/null +++ b/test/attr/typed_throws_availability_osx.swift @@ -0,0 +1,15 @@ +// RUN: %swift -typecheck -verify -target x86_64-apple-macosx10.10 %s -enable-experimental-feature TypedThrows + +// REQUIRES: OS=macosx + +@available(macOS 12, *) +enum MyError: Error { + case fail +} + +@available(macOS 11, *) +func throwMyErrorBadly() throws(MyError) { } +// expected-error@-1{{'MyError' is only available in macOS 12 or newer}} + + + diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index d8f7bf7f44e6..8682f3579b91 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -1,5 +1,6 @@ // RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test -enable-experimental-feature TypedThrows +// expected-note@+1{{type declared here}} enum MyError: Error { case fail } @@ -60,3 +61,6 @@ func testThrowingInFunction(cond: Bool, cond2: Bool) throws(MyError) { // expected-error@-1{{thrown expression type 'MyBadError' cannot be converted to error type 'MyError'}} } } + +public func testThrowingInternal() throws(MyError) { } +// expected-error@-1{{function cannot be declared public because its thrown error uses an internal type}} From b6b999abd486e3b8d6d933e4429a10d82d0d6180 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Sep 2023 10:22:28 -0700 Subject: [PATCH 6/8] [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. --- include/swift/AST/Decl.h | 7 +++ include/swift/AST/Expr.h | 7 +++ include/swift/AST/Types.h | 10 +++- lib/AST/ASTVerifier.cpp | 12 +++-- lib/AST/Decl.cpp | 12 +++++ lib/AST/Expr.cpp | 5 ++ lib/AST/Type.cpp | 18 +++++++ lib/SIL/IR/SILFunctionType.cpp | 8 +++- lib/SILGen/SILGenBackDeploy.cpp | 3 +- lib/SILGen/SILGenBridging.cpp | 8 ++-- lib/SILGen/SILGenConstructor.cpp | 11 +++-- lib/SILGen/SILGenDestructor.cpp | 8 ++-- lib/SILGen/SILGenEpilog.cpp | 14 +++--- lib/SILGen/SILGenFunction.cpp | 11 +++-- lib/SILGen/SILGenFunction.h | 8 ++-- lib/SILGen/SILGenStmt.cpp | 39 ++++++++++++++- lib/SILGen/SILGenTopLevel.cpp | 3 +- test/SILGen/typed_throws.swift | 82 ++++++++++++++++++++++++++++++++ 18 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 test/SILGen/typed_throws.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 64927185b4e7..96f438bdd9d7 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -7066,6 +7066,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Retrieves the thrown interface type. Type getThrownInterfaceType() const; + /// Retrieve the "effective" thrown interface type, or llvm::None if + /// this function cannot throw. + /// + /// Functions with untyped throws will produce "any Error", functions that + /// cannot throw or are specified to throw "Never" will return llvm::None. + llvm::Optional getEffectiveThrownInterfaceType() const; + /// Returns if the function throws or is async. bool hasEffect(EffectKind kind) const; diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 79dcf45537c1..59d8ea864691 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -3847,6 +3847,13 @@ class AbstractClosureExpr : public DeclContext, public Expr { /// Return whether this closure is throwing when fully applied. bool isBodyThrowing() const; + /// Retrieve the "effective" thrown interface type, or llvm::None if + /// this closure cannot throw. + /// + /// Closures with untyped throws will produce "any Error", functions that + /// cannot throw or are specified to throw "Never" will return llvm::None. + llvm::Optional getEffectiveThrownType() const; + /// \brief Return whether this closure is async when fully applied. bool isBodyAsync() const; diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index b69fafbc55f8..80f3811f9d91 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -3386,6 +3386,13 @@ class AnyFunctionType : public TypeBase { Type getGlobalActor() const; Type getThrownError() const; + /// Retrieve the "effective" thrown interface type, or llvm::None if + /// this function cannot throw. + /// + /// Functions with untyped throws will produce "any Error", functions that + /// cannot throw or are specified to throw "Never" will return llvm::None. + llvm::Optional getEffectiveThrownInterfaceType() const; + /// Returns true if the function type stores a Clang type that cannot /// be derived from its Swift type. Returns false otherwise, including if /// the function type is not @convention(c) or @convention(block). @@ -3612,7 +3619,8 @@ BEGIN_CAN_TYPE_WRAPPER(AnyFunctionType, Type) } PROXY_CAN_TYPE_SIMPLE_GETTER(getResult) - + PROXY_CAN_TYPE_SIMPLE_GETTER(getThrownError) + CanAnyFunctionType withExtInfo(ExtInfo info) const { return CanAnyFunctionType(getPointer()->withExtInfo(info)); } diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 575b724e8610..289f2173a219 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1014,9 +1014,15 @@ class Verifier : public ASTWalker { } void verifyChecked(ThrowStmt *S) { - checkSameType(S->getSubExpr()->getType(), - checkExceptionTypeExists("throw expression"), - "throw operand"); + Type thrownError; + if (!Functions.empty()) { + if (auto fn = AnyFunctionRef::fromDeclContext(Functions.back())) + thrownError = fn->getThrownErrorType(); + } + + if (!thrownError) + thrownError = checkExceptionTypeExists("throw expression"); + checkSameType(S->getSubExpr()->getType(), thrownError, "throw operand"); verifyCheckedBase(S); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c3dafe68d4fe..ee931eb1f740 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -952,6 +952,18 @@ Type AbstractFunctionDecl::getThrownInterfaceType() const { Type()); } +llvm::Optional +AbstractFunctionDecl::getEffectiveThrownInterfaceType() const { + Type interfaceType = getInterfaceType(); + if (hasImplicitSelfDecl()) { + if (auto fnType = interfaceType->getAs()) + interfaceType = fnType->getResult(); + } + + return interfaceType->castTo() + ->getEffectiveThrownInterfaceType(); +} + Expr *AbstractFunctionDecl::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); auto braceStmt = getBody(); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e481838077b9..0db9de12dda6 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1930,6 +1930,11 @@ Type AbstractClosureExpr::getResultType( return T->castTo()->getResult(); } +llvm::Optional AbstractClosureExpr::getEffectiveThrownType() const { + return getType()->castTo() + ->getEffectiveThrownInterfaceType(); +} + bool AbstractClosureExpr::isBodyThrowing() const { if (!getType() || getType()->hasError()) { // Scan the closure body to infer effects. diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f1721dfe50b7..58a4057d35da 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -5419,6 +5419,24 @@ AnyFunctionType *AnyFunctionType::getWithoutThrowing() const { return withExtInfo(info); } +llvm::Optional AnyFunctionType::getEffectiveThrownInterfaceType() const { + // A non-throwing function... has no thrown interface type. + if (!isThrowing()) + return llvm::None; + + // If there is no specified thrown error type, it throws "any Error". + Type thrownError = getThrownError(); + if (!thrownError) + return getASTContext().getErrorExistentialType(); + + // If the thrown interface type is "Never", this function does not throw. + if (thrownError->isEqual(getASTContext().getNeverType())) + return llvm::None; + + // Otherwise, return the typed error. + return thrownError; +} + llvm::Optional TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) { assert(lookupConformance); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 4f9f3f875154..b76b24512020 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2202,7 +2202,13 @@ static CanSILFunctionType getSILFunctionType( !foreignInfo.async) { assert(!origType.isForeign() && "using native Swift error convention for foreign type!"); - SILType exnType = SILType::getExceptionType(TC.Context); + SILType exnType; + if (CanType thrownError = substFnInterfaceType.getThrownError()) { + exnType = TC.getLoweredType(thrownError, expansionContext); + } else { + // Untyped error throws the exception type. + exnType = SILType::getExceptionType(TC.Context); + } assert(exnType.isObject()); errorResult = SILResultInfo(exnType.getASTType(), ResultConvention::Owned); diff --git a/lib/SILGen/SILGenBackDeploy.cpp b/lib/SILGen/SILGenBackDeploy.cpp index 402cbeba4770..b2e02d479ea9 100644 --- a/lib/SILGen/SILGenBackDeploy.cpp +++ b/lib/SILGen/SILGenBackDeploy.cpp @@ -237,7 +237,8 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) { paramsForForwarding.emplace_back(param.forward(*this)); } - prepareEpilog(getResultInterfaceType(AFD), AFD->hasThrows(), + prepareEpilog(getResultInterfaceType(AFD), + AFD->getEffectiveThrownInterfaceType(), CleanupLocation(AFD)); SILBasicBlock *availableBB = createBasicBlock("availableBB"); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index f11bc67422be..c413a0d56a19 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -2081,7 +2081,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { auto foreignFnTy = foreignCI.SILFnType; // Find the foreign error/async convention and 'self' parameter index. - bool hasError = false; + llvm::Optional thrownErrorType; llvm::Optional foreignAsync; if (nativeFnTy->isAsync()) { foreignAsync = fd->getForeignAsyncConvention(); @@ -2089,7 +2089,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { } llvm::Optional foreignError; if (nativeFnTy->hasErrorResult()) { - hasError = true; + thrownErrorType = nativeFnTy->getErrorResult().getInterfaceType(); foreignError = fd->getForeignErrorConvention(); assert((foreignError || foreignAsync) && "couldn't find foreign error or async convention for foreign error!"); @@ -2133,8 +2133,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { // Set up the throw destination if necessary. CleanupLocation cleanupLoc(fd); - if (hasError) { - prepareRethrowEpilog(cleanupLoc); + if (thrownErrorType) { + prepareRethrowEpilog(*thrownErrorType, cleanupLoc); } SILValue result; diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 748447c84cee..5abaa603df66 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -666,7 +666,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { // Create a basic block to jump to for the implicit 'self' return. // We won't emit this until after we've emitted the body. // The epilog takes a void return because the return of 'self' is implicit. - prepareEpilog(llvm::None, ctor->hasThrows(), CleanupLocation(ctor)); + prepareEpilog(llvm::None, ctor->getEffectiveThrownInterfaceType(), + CleanupLocation(ctor)); // If the constructor can fail, set up an alternative epilog for constructor // failure. @@ -1170,7 +1171,8 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // Create a basic block to jump to for the implicit 'self' return. // We won't emit the block until after we've emitted the body. - prepareEpilog(llvm::None, ctor->hasThrows(), CleanupLocation(endOfInitLoc)); + prepareEpilog(llvm::None, ctor->getEffectiveThrownInterfaceType(), + CleanupLocation(endOfInitLoc)); auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); @@ -1643,7 +1645,7 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { VarLocs[selfDecl] = VarLoc::get(selfArg); auto cleanupLoc = CleanupLocation(loc); - prepareEpilog(llvm::None, false, cleanupLoc); + prepareEpilog(llvm::None, llvm::None, cleanupLoc); // Emit the initializers. emitMemberInitializers(cd, selfDecl, cd); @@ -1713,7 +1715,8 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { } } - prepareEpilog(accessor->getResultInterfaceType(), accessor->hasThrows(), + prepareEpilog(accessor->getResultInterfaceType(), + accessor->getEffectiveThrownInterfaceType(), CleanupLocation(accessor)); emitProfilerIncrement(accessor->getTypecheckedBody()); diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 9c46adb53171..920947101233 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -45,7 +45,7 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, false, CleanupLocation(Loc)); + prepareEpilog(llvm::None, llvm::None, CleanupLocation(Loc)); auto cleanupLoc = CleanupLocation(Loc); @@ -248,7 +248,7 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, false, CleanupLocation(loc)); + prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc)); auto cleanupLoc = CleanupLocation(loc); @@ -286,7 +286,7 @@ void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) { assert(selfValue); auto cleanupLoc = CleanupLocation(loc); - prepareEpilog(llvm::None, false, cleanupLoc); + prepareEpilog(llvm::None, llvm::None, cleanupLoc); { Scope S(*this, cleanupLoc); // Self is effectively guaranteed for the duration of any destructor. For @@ -577,7 +577,7 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, false, CleanupLocation(loc)); + prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc)); emitProfilerIncrement(dd->getTypecheckedBody()); // Emit the destructor body. diff --git a/lib/SILGen/SILGenEpilog.cpp b/lib/SILGen/SILGenEpilog.cpp index 1d57122f2c36..4639bbf388ec 100644 --- a/lib/SILGen/SILGenEpilog.cpp +++ b/lib/SILGen/SILGenEpilog.cpp @@ -21,7 +21,8 @@ using namespace swift; using namespace Lowering; void SILGenFunction::prepareEpilog(llvm::Optional directResultType, - bool isThrowing, CleanupLocation CleanupL) { + llvm::Optional exnType, + CleanupLocation CleanupL) { auto *epilogBB = createBasicBlock(); // If we have any direct results, receive them via BB arguments. @@ -62,8 +63,8 @@ void SILGenFunction::prepareEpilog(llvm::Optional directResultType, ReturnDest = JumpDest(epilogBB, getCleanupsDepth(), CleanupL); - if (isThrowing) { - prepareRethrowEpilog(CleanupL); + if (exnType) { + prepareRethrowEpilog(*exnType, CleanupL); } if (F.getLoweredFunctionType()->isCoroutine()) { @@ -71,10 +72,11 @@ void SILGenFunction::prepareEpilog(llvm::Optional directResultType, } } -void SILGenFunction::prepareRethrowEpilog(CleanupLocation cleanupLoc) { - auto exnType = SILType::getExceptionType(getASTContext()); +void SILGenFunction::prepareRethrowEpilog( + Type exnType, CleanupLocation cleanupLoc) { + SILType loweredExnType = getLoweredType(exnType); SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter); - rethrowBB->createPhiArgument(exnType, OwnershipKind::Owned); + rethrowBB->createPhiArgument(loweredExnType, OwnershipKind::Owned); ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 36152d622b01..a7a350d41c83 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1051,7 +1051,7 @@ void SILGenFunction::emitFunction(FuncDecl *fd) { emitDistributedActorFactory(fd); } else { prepareEpilog(fd->getResultInterfaceType(), - fd->hasThrows(), CleanupLocation(fd)); + fd->getEffectiveThrownInterfaceType(), CleanupLocation(fd)); if (fd->requiresUnavailableDeclABICompatibilityStubs()) emitApplyOfUnavailableCodeReached(); @@ -1077,7 +1077,8 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitProlog(captureInfo, ace->getParameters(), /*selfParam=*/nullptr, ace, resultIfaceTy, ace->isBodyThrowing(), ace->getLoc(), OrigFnType); - prepareEpilog(resultIfaceTy, ace->isBodyThrowing(), CleanupLocation(ace)); + prepareEpilog(resultIfaceTy, ace->getEffectiveThrownType(), + CleanupLocation(ace)); emitProfilerIncrement(ace); if (auto *ce = dyn_cast(ace)) { @@ -1559,7 +1560,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, // been recorded for this expression, not the sub-expression. emitProfilerIncrement(topLevelValue); } - prepareEpilog(interfaceType, false, CleanupLocation(Loc)); + prepareEpilog(interfaceType, llvm::None, CleanupLocation(Loc)); { llvm::Optional opaqueValue; @@ -1622,7 +1623,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { emitBasicProlog(/*paramList*/ nullptr, /*selfParam*/ nullptr, interfaceType, dc, /*throws=*/ false,SourceLoc(), /*ignored parameters*/ 0); - prepareEpilog(interfaceType, false, CleanupLocation(loc)); + prepareEpilog(interfaceType, llvm::None, CleanupLocation(loc)); auto pbd = var->getParentPatternBinding(); const auto i = pbd->getPatternEntryIndexForVarDecl(var); @@ -1678,7 +1679,7 @@ void SILGenFunction::emitGeneratorFunction( /*selfParam=*/nullptr, dc, resultInterfaceType, /*throws=*/false, SourceLoc(), pattern); - prepareEpilog(resultInterfaceType, /*hasThrows=*/false, CleanupLocation(loc)); + prepareEpilog(resultInterfaceType, llvm::None, CleanupLocation(loc)); emitStmt(body); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 77520d513c21..c4353dc9a212 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1172,12 +1172,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// \param directResultType If given a value, the epilog block will be /// created with arguments for each direct result of this /// function, corresponding to the formal return type. - /// \param isThrowing If true, create an error epilog block. + /// \param exnType If not None, create an error epilog block with the given + /// exception type. /// \param L The SILLocation which should be associated with /// cleanup instructions. - void prepareEpilog(llvm::Optional directResultType, bool isThrowing, + void prepareEpilog(llvm::Optional directResultType, + llvm::Optional exnType, CleanupLocation L); - void prepareRethrowEpilog(CleanupLocation l); + void prepareRethrowEpilog(Type exnType, CleanupLocation l); void prepareCoroutineUnwindEpilog(CleanupLocation l); /// Branch to and emit the epilog basic block. This will fuse diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index f373a739c263..ee041ccaf18c 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -1504,13 +1504,50 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV, // But for now we aren't bothering. SILValue exn = exnMV.forward(*this); - if (emitWillThrow) { + // Whether the thrown exception is already an Error existential box. + SILType existentialBoxType = SILType::getExceptionType(getASTContext()); + bool isExistentialBox = exn->getType() == existentialBoxType; + + // FIXME: Right now, we suppress emission of the willThrow builtin if the + // error isn't already the error existential, because swift_willThrow expects + // the existential box. + if (emitWillThrow && isExistentialBox) { // Generate a call to the 'swift_willThrow' runtime function to allow the // debugger to catch the throw event. B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"), SGM.Types.getEmptyTupleType(), {}, {exn}); } + // If we don't have an existential box, create one to jump to the throw + // destination. + SILBasicBlock &throwBB = *ThrowDest.getBlock(); + if (!throwBB.getArguments().empty()) { + auto errorArg = throwBB.getArguments()[0]; + SILType errorArgType = errorArg->getType(); + if (exn->getType() != errorArgType) { + assert(errorArgType == existentialBoxType); + + // FIXME: Callers should provide this conformance from places recorded in + // the AST. + ProtocolConformanceRef conformances[1] = { + getModule().getSwiftModule()->conformsToProtocol( + exn->getType().getASTType(), getASTContext().getErrorDecl()) + }; + exnMV = emitExistentialErasure( + loc, + exn->getType().getASTType(), + getTypeLowering(exn->getType()), + getTypeLowering(existentialBoxType), + getASTContext().AllocateCopy(conformances), + SGFContext(), + [&](SGFContext C) -> ManagedValue { + return ManagedValue::forForwardedRValue(*this, exn); + }); + + exn = exnMV.forward(*this); + } + } + // Branch to the cleanup destination. Cleanups.emitBranchAndCleanups(ThrowDest, loc, exn, IsForUnwind); } diff --git a/lib/SILGen/SILGenTopLevel.cpp b/lib/SILGen/SILGenTopLevel.cpp index ad56c85017ee..54bd02808e33 100644 --- a/lib/SILGen/SILGenTopLevel.cpp +++ b/lib/SILGen/SILGenTopLevel.cpp @@ -41,7 +41,8 @@ void SILGenModule::emitEntryPoint(SourceFile *SF, SILFunction *TopLevel) { TopLevelSGF.MagicFunctionName = SwiftModule->getName(); auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation(); - TopLevelSGF.prepareEpilog(llvm::None, true, moduleCleanupLoc); + TopLevelSGF.prepareEpilog( + llvm::None, getASTContext().getErrorExistentialType(), moduleCleanupLoc); auto prologueLoc = RegularLocation::getModuleLocation(); prologueLoc.markAsPrologue(); diff --git a/test/SILGen/typed_throws.swift b/test/SILGen/typed_throws.swift new file mode 100644 index 000000000000..8df4844f0588 --- /dev/null +++ b/test/SILGen/typed_throws.swift @@ -0,0 +1,82 @@ +// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows | %FileCheck %s + +enum MyError: Error { + case fail +} + +enum MyBigError: Error { + case epicFail +} + +func throwsMyBigError() throws(MyBigError) { } + +// CHECK: sil hidden [ossa] @$s12typed_throws20doesNotThrowConcreteyyKF : $@convention(thin) () -> @error MyError +func doesNotThrowConcrete() throws(MyError) { } + +// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B8ConcreteyyKF : $@convention(thin) () -> @error MyError +func throwsConcrete() throws(MyError) { + // CHECK: [[ERROR:%[0-9]+]] = enum $MyError, #MyError.fail!enumelt + // CHECK-NOT: builtin "willThrow" + // CHECK: throw [[ERROR]] : $MyError + throw .fail +} + +// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws15rethrowConcreteyyKF +func rethrowConcrete() throws(MyError) { + // CHECK: try_apply [[FN:%[0-9]+]]() : $@convention(thin) () -> @error MyError, normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] + // CHECK: [[ERRORBB]]([[ERROR:%[0-9]+]] : $MyError) + // CHECK-NEXT: throw [[ERROR]] : $MyError + try throwsConcrete() +} +// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws29rethrowConcreteAndExistentialyyKF +func rethrowConcreteAndExistential() throws { + // CHECK: try_apply [[FN:%[0-9]+]]() : $@convention(thin) () -> @error MyError, normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] + // CHECK: alloc_existential_box $any Error, $MyError + // CHECK: throw [[ERROR:%[0-9]+]] : $any Error + try throwsConcrete() +} + + +func sink(_: T) { } + +// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B27OneOrTheOtherWithoutRethrowyyF +func throwsOneOrTheOtherWithoutRethrow() { + do { + // FIXME: the generated code below could probably be better, because it + // currently depends on injecting into an existential error. + + // CHECK: try_apply [[F1:%[0-9]+]]() : $@convention(thin) () -> @error MyError, normal [[F1_NORMAL:bb[0-9]+]], error [[F1_ERROR:bb[0-9]+]] + try doesNotThrowConcrete() + + // CHECK: try_apply [[F2:%[0-9]+]]() : $@convention(thin) () -> @error MyError, normal [[F2_NORMAL:bb[0-9]+]], error [[F2_ERROR:bb[0-9]+]] + try throwsConcrete() + try throwsMyBigError() + } catch let e as MyError { + sink(e) + } catch let be as MyBigError { + sink(be) + } catch { + fatalError("not actually reachable") + } +} + +// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B24OneOrTheOtherWithRethrowyyKF +func throwsOneOrTheOtherWithRethrow() throws { + do { + // FIXME: This probably shouldn't even have a rethrow block, because the + // type checker Should Know that this list of catch clauses is exhaustive. + // For now, we check that there is a basic block that takes an existential + // error, but we want that to go away. + // + // CHECK: return [[RET:%[0-9]+]] : $() + // CHECK-NOT: return + // CHECK: @owned $any Error + // CHECK: throw + try throwsConcrete() + try throwsMyBigError() + } catch let e as MyError { + sink(e) + } catch let be as MyBigError { + sink(be) + } +} From 1d976a3754491185d41387d740395a2944ae8b12 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Sep 2023 11:56:34 -0700 Subject: [PATCH 7/8] [Typed throws] Map the experimental feature flag over to the new parser's flag --- lib/ASTGen/Sources/ASTGen/Decls.swift | 1 + lib/ASTGen/Sources/ASTGen/SourceFile.swift | 1 + lib/ASTGen/Sources/ASTGen/Types.swift | 1 + 3 files changed, 3 insertions(+) diff --git a/lib/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index 141e1a323377..c36ce39f0e08 100644 --- a/lib/ASTGen/Sources/ASTGen/Decls.swift +++ b/lib/ASTGen/Sources/ASTGen/Decls.swift @@ -2,6 +2,7 @@ import CASTBridging // Needed to use SyntaxTransformVisitor's visit method. @_spi(SyntaxTransformVisitor) +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax import SwiftDiagnostics diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index 919238072ea8..15cda3f56433 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceFile.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceFile.swift @@ -36,6 +36,7 @@ extension Parser.ExperimentalFeatures { } } mapFeature(.ThenStatements, to: .thenStatements) + mapFeature(.TypedThrows, to: .typedThrows) } } diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index 97904f1251ad..0989cbf8ef7e 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -2,6 +2,7 @@ import CASTBridging // Needed to use SyntaxTransformVisitor's visit method. @_spi(SyntaxTransformVisitor) +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax import SwiftDiagnostics From e51faf20ab826d71a3427786e747e84eeb777d69 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 29 Sep 2023 17:30:12 -0700 Subject: [PATCH 8/8] Adjust to typed-throws syntax node renames --- lib/ASTGen/Sources/ASTGen/Decls.swift | 4 ++-- lib/ASTGen/Sources/ASTGen/Types.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index c36ce39f0e08..a3cb0ef7466f 100644 --- a/lib/ASTGen/Sources/ASTGen/Decls.swift +++ b/lib/ASTGen/Sources/ASTGen/Decls.swift @@ -256,7 +256,7 @@ extension ASTGenVisitor { parameterList: self.visit(node.signature.parameterClause).rawValue, asyncSpecifierLoc: (node.signature.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), throwsSpecifierLoc: (node.signature.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), - thrownType: self.visit(node.signature.effectSpecifiers?.thrownType?.type)?.rawValue, + thrownType: self.visit(node.signature.effectSpecifiers?.thrownError?.type)?.rawValue, returnType: self.visit(node.signature.returnClause?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) @@ -281,7 +281,7 @@ extension ASTGenVisitor { parameterList: self.visit(node.signature.parameterClause).rawValue, asyncSpecifierLoc: (node.signature.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), throwsSpecifierLoc: (node.signature.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), - thrownType: self.visit(node.signature.effectSpecifiers?.thrownType?.type)?.rawValue, + thrownType: self.visit(node.signature.effectSpecifiers?.thrownError?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index 0989cbf8ef7e..8c557002e4cb 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -160,7 +160,7 @@ extension ASTGenVisitor { ), (node.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), (node.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), - self.visit(node.effectSpecifiers?.thrownType?.type)?.rawValue, + self.visit(node.effectSpecifiers?.thrownError?.type)?.rawValue, node.returnClause.arrow.bridgedSourceLoc(in: self), visit(node.returnClause.type).rawValue )