diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index ce2510a6194ca..b22b3c88d86e7 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/CASTBridging.h b/include/swift/AST/CASTBridging.h index 84bf3cf500a2e..8d2256b91a747 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 dba332e23d3ce..96f438bdd9d7f 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 @@ -6942,6 +6943,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 +6954,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 +7058,21 @@ 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; + + /// 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; @@ -7443,12 +7463,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 +7494,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 +7518,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 +7526,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 +7534,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 +7675,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 +7693,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 +7710,7 @@ class AccessorDecl final : public FuncDecl { AbstractStorageDecl *storage, StaticSpellingKind staticSpelling, bool async, bool throws, + Type thrownType, Type fnRetType, DeclContext *parent); static AccessorDecl * @@ -7689,7 +7718,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 +8082,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 +8093,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 6e103aba20d48..031eb93099241 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/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 432ecdcc1e0ff..1de62bb5b1ee1 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)) @@ -2265,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' " @@ -2280,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 " @@ -5048,6 +5050,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/Expr.h b/include/swift/AST/Expr.h index 783f1e88f5fce..59d8ea8646919 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; @@ -3981,6 +3988,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 +4001,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 +4102,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 +5205,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/ExtInfo.h b/include/swift/AST/ExtInfo.h index 7a8a0975cfe71..fb196dbb2c46b 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 a0df8721e362c..0cf60d82bb7e3 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 SimpleRequest 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/AST/Types.h b/include/swift/AST/Types.h index c00a004f2923e..80f3811f9d911 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -407,13 +407,14 @@ class alignas(1 << TypeAlignInBits) TypeBase SWIFT_INLINE_BITFIELD_EMPTY(ParenType, SugarType); - SWIFT_INLINE_BITFIELD_FULL(AnyFunctionType, TypeBase, NumAFTExtInfoBits+1+1+1+16, + SWIFT_INLINE_BITFIELD_FULL(AnyFunctionType, TypeBase, NumAFTExtInfoBits+1+1+1+1+16, /// Extra information which affects how the function is called, like /// regparm and the calling convention. ExtInfoBits : NumAFTExtInfoBits, HasExtInfo : 1, HasClangTypeInfo : 1, HasGlobalActor : 1, + HasThrownError : 1, : NumPadBits, NumParams : 16 ); @@ -3319,6 +3320,8 @@ class AnyFunctionType : public TypeBase { !Info.value().getClangTypeInfo().empty(); Bits.AnyFunctionType.HasGlobalActor = !Info.value().getGlobalActor().isNull(); + Bits.AnyFunctionType.HasThrownError = + !Info.value().getThrownError().isNull(); // The use of both assert() and static_assert() is intentional. assert(Bits.AnyFunctionType.ExtInfoBits == Info.value().getBits() && "Bits were dropped!"); @@ -3330,6 +3333,7 @@ class AnyFunctionType : public TypeBase { Bits.AnyFunctionType.HasClangTypeInfo = false; Bits.AnyFunctionType.ExtInfoBits = 0; Bits.AnyFunctionType.HasGlobalActor = false; + Bits.AnyFunctionType.HasThrownError = false; } Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); @@ -3372,11 +3376,23 @@ class AnyFunctionType : public TypeBase { return Bits.AnyFunctionType.HasGlobalActor; } + bool hasThrownError() const { + return Bits.AnyFunctionType.HasThrownError; + } + ClangTypeInfo getClangTypeInfo() const; ClangTypeInfo getCanonicalClangTypeInfo() const; 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). @@ -3400,23 +3416,14 @@ class AnyFunctionType : public TypeBase { ExtInfo getExtInfo() const { assert(hasExtInfo()); return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangTypeInfo(), - getGlobalActor()); + getGlobalActor(), getThrownError()); } /// Get the canonical ExtInfo for the function type. /// /// The parameter useClangFunctionType is present only for staging purposes. /// In the future, we will always use the canonical clang function type. - ExtInfo getCanonicalExtInfo(bool useClangFunctionType) const { - assert(hasExtInfo()); - Type globalActor = getGlobalActor(); - if (globalActor) - globalActor = globalActor->getCanonicalType(); - return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, - useClangFunctionType ? getCanonicalClangTypeInfo() - : ClangTypeInfo(), - globalActor); - } + ExtInfo getCanonicalExtInfo(bool useClangFunctionType) const; bool hasSameExtInfoAs(const AnyFunctionType *otherFn); @@ -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)); } @@ -3642,7 +3650,7 @@ class FunctionType final } size_t numTrailingObjects(OverloadToken) const { - return hasGlobalActor() ? 1 : 0; + return hasGlobalActor() + hasThrownError(); } public: @@ -3667,9 +3675,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 +3788,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 +3810,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 9867564cb9ff0..5f72bda34a33f 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/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 87db5e5111ade..b3b641b4f24cb 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/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 82e6b3ab922f6..feee1045599b6 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/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 223d86ba29470..8a99511b0e57f 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 5470a4becc8b8..80659a10c9608 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 f778373189e0a..a909f215d2624 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); @@ -4175,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 296db40c09f77..fb74f11abe2eb 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(); @@ -4404,8 +4432,10 @@ void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { if (AFD->hasThrows()) { if (AFD->getAttrs().hasAttribute()) Printer << " " << tok::kw_rethrows; - else + else { Printer << " " << tok::kw_throws; + printThrownErrorIfNecessary(AFD); + } } } } @@ -4470,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); } @@ -6709,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 << " -> "; @@ -6756,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/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 575b724e8610f..289f2173a2196 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/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 805f142f2eeb2..3888b8a6755e1 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 5ce1ff2f7b910..aaed1cc21093e 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); @@ -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/CASTBridging.cpp b/lib/AST/CASTBridging.cpp index 7ede52564cbc0..e4c5ea69a6e79 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 4330fd2d9ae17..ee931eb1f7400 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -945,6 +945,25 @@ bool Decl::preconcurrency() const { return false; } +Type AbstractFunctionDecl::getThrownInterfaceType() const { + return evaluateOrDefault( + getASTContext().evaluator, + ThrownTypeRequest{const_cast(this)}, + 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(); @@ -3228,7 +3247,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(); } @@ -9477,6 +9496,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 +9508,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 +9521,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 +9539,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 +9555,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 +9572,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 +9610,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 +9621,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 +9632,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 +9649,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 +9804,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 +9828,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 +9837,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 +9861,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 dd3fe9f7ac524..0db9de12dda6f 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. @@ -2035,6 +2040,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/Type.cpp b/lib/AST/Type.cpp index 05e736315a719..58a4057d35daa 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,10 +5415,28 @@ 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); } +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/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index ff09ea2e9ba47..8b35223cb9b5a 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/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 2f794ee07aafa..6a971d9ec0e13 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/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index 9496a304dc80c..5df2a8dd5328d 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/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index 5927fd60db331..a3cb0ef7466f9 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 @@ -255,6 +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?.thrownError?.type)?.rawValue, returnType: self.visit(node.signature.returnClause?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) @@ -279,6 +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?.thrownError?.type)?.rawValue, genericWhereClause: self.visit(node.genericWhereClause)?.rawValue ) diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index 919238072ea8a..15cda3f56433b 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 fa4efae600d6f..8c557002e4cb1 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 @@ -159,6 +160,7 @@ extension ASTGenVisitor { ), (node.effectSpecifiers?.asyncSpecifier).bridgedSourceLoc(in: self), (node.effectSpecifiers?.throwsSpecifier).bridgedSourceLoc(in: self), + self.visit(node.effectSpecifiers?.thrownError?.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 f2539d9b54ee4..e88ddd224d127 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 844ad28eb32ff..05471b48aeb74 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 93791b8044905..f972f5b407e38 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 10251f3e5aeff..f76a3d161a276 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 4c99fc97e08b1..da4b921e1042d 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 8d5da84a4a087..7e51b987cd5b7 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 2346e4169392d..8b7ee6fdb9513 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 3d6d9b34f8bc5..982a07352bf23 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/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index a5a6faf6d3b3b..b76b245120202 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); @@ -4627,13 +4633,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 e2d36a3a76329..0bf06114d500d 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/SILGenBackDeploy.cpp b/lib/SILGen/SILGenBackDeploy.cpp index 402cbeba4770e..b2e02d479ea96 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 f11bc67422be3..c413a0d56a190 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 748447c84cee1..5abaa603df662 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 9c46adb531712..9209471012336 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 1d57122f2c362..4639bbf388ecf 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/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 96df23508ad91..373b9009518c2 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/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 02afa1b9bdf89..a7a350d41c832 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)) { @@ -1481,7 +1482,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)); @@ -1558,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; @@ -1621,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); @@ -1677,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 77520d513c216..c4353dc9a2125 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/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 7408c6c0890a0..bfaa7d6005ef0 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/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index f373a739c263b..ee041ccaf18cc 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 ad56c85017ee0..54bd02808e33e 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/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 61b2c0eae84d5..74bdf7cdf9843 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/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 0f4a947a3b220..d25fe6fc107a2 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/CSGen.cpp b/lib/Sema/CSGen.cpp index 250ea5dcc8934..fc533690857cb 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 9ef4c791ec108..b8798901a1759 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/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 1f8b711fec167..683e1f8fc2cb1 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 31748c5926f21..6521952b1057e 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/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 8a8d3f95f2371..6af5e64792553 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 595cf3de97093..f8ec5c52b6edd 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(); @@ -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/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 9e18da410776e..978523b4e0379 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 29379d381b32a..f637f71281abf 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 98d9920620bae..ee042850456d6 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 635e152e421af..f02ca266442d5 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 22f9367f74263..526949ad356f1 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 fbeba88e4f8a8..852c54e624a37 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 20d893565c37c..216ffadd18784 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 11517a4c87ec9..2940eaadc1d47 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 68d36ea043093..3d2384c416609 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 d8d7efc3c6eed..e735161a0f369 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 66abb7dc2ba95..f55f72caf8aca 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/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index f3c9c152f12ce..caad0f1e71b21 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/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d559dc086d3c4..e0a722250d6fe 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/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c06b6119640db..3c609b1ced177 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 b1fae03d75b0f..b8b3f8ac6466f 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 9aa62497aecb4..3da9bce843d76 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/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d676a6ff3fa09..154a25dc3bdc6 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1195,10 +1195,18 @@ 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/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 913ae40523136..8335c7d904cd3 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/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index dc58ab8afc4e2..b2a74c0c4196f 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 0a4f8362eefd1..0111f18cee304 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 2e7b646a2d7eb..76015325ac4ab 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; @@ -6464,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; @@ -6471,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; } @@ -6485,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(); @@ -6507,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 b2afac5e5ce2a..006913ddf5e8e 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. /// @@ -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 @@ -1505,6 +1507,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 +1582,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 +1644,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 ece40f192f4b6..fc4393b45d50c 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( @@ -5341,6 +5344,7 @@ class Serializer::TypeSerializer : public TypeVisitor { fnTy->isSendable(), fnTy->isAsync(), fnTy->isThrowing(), + S.addTypeRef(fnTy->getThrownError()), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), globalActor); @@ -5356,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 0000000000000..ea2831fe4c6e4 --- /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 new file mode 100644 index 0000000000000..84e95f53a749d --- /dev/null +++ b/test/Parse/typed_throws.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test -enable-experimental-feature TypedThrows + +// 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/test/SILGen/typed_throws.swift b/test/SILGen/typed_throws.swift new file mode 100644 index 0000000000000..8df4844f05889 --- /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) + } +} diff --git a/test/Serialization/Inputs/def_typed_throws.swift b/test/Serialization/Inputs/def_typed_throws.swift new file mode 100644 index 0000000000000..89ba6ea499a76 --- /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 0000000000000..2e94f81321fa6 --- /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/attr/typed_throws_availability_osx.swift b/test/attr/typed_throws_availability_osx.swift new file mode 100644 index 0000000000000..7edc67eecd02b --- /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 new file mode 100644 index 0000000000000..8682f3579b918 --- /dev/null +++ b/test/decl/func/typed_throws.swift @@ -0,0 +1,66 @@ +// 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 +} + +enum MyOtherError: 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) -> ()'}} + + 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'}} + } +} + +public func testThrowingInternal() throws(MyError) { } +// expected-error@-1{{function cannot be declared public because its thrown error uses an internal type}} diff --git a/test/expr/closure/typed_throws.swift b/test/expr/closure/typed_throws.swift new file mode 100644 index 0000000000000..58478c3afdba4 --- /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 -> ()'}} +} + diff --git a/unittests/Sema/ConstraintGenerationTests.cpp b/unittests/Sema/ConstraintGenerationTests.cpp index 384114f7a3409..18d65b543d823 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 e002aed57a9c4..00a05e23ff646 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,