From 9f5a754b77181df6ddb032a3a0d58ba7b8520c5b Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 17 Sep 2025 15:23:08 +0100 Subject: [PATCH] [Sema] Ban placeholders in typed throws This never worked correctly and would crash in SILGen, ban the use of placeholder types. While here, ensure we replace any ErrorTypes with holes when solving the closure in the constraint system. --- lib/Sema/CSGen.cpp | 7 +++++-- lib/Sema/TypeCheckType.cpp | 6 +++--- test/decl/func/typed_throws.swift | 5 +++++ test/expr/closure/typed_throws.swift | 5 +++++ test/stmt/typed_throws.swift | 11 +++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0d1b17a189d..91adbaf8cf4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2542,8 +2542,11 @@ namespace { Type thrownErrorTy = [&] { // Explicitly-specified thrown type. if (closure->getExplicitThrownTypeRepr()) { - if (Type explicitType = closure->getExplicitThrownType()) - return explicitType; + if (Type explicitType = closure->getExplicitThrownType()) { + // The thrown type may have errors, open as placeholders if needed. + return CS.replaceInferableTypesWithTypeVars(explicitType, + thrownErrorLocator); + } } // Explicitly-specified 'throws' without a type is untyped throws. diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 30698e39228..a9c7815541f 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -6999,7 +6999,7 @@ Type ExplicitCaughtTypeRequest::evaluate( return TypeResolution::forInterface(func, options, /*unboundTyOpener*/ nullptr, - PlaceholderType::get, + /*placeholderOpener*/ nullptr, /*packElementOpener*/ nullptr) .resolveType(thrownTypeRepr); } @@ -7011,7 +7011,7 @@ Type ExplicitCaughtTypeRequest::evaluate( return TypeResolution::resolveContextualType( thrownTypeRepr, closure, TypeResolutionOptions(TypeResolverContext::None), - /*unboundTyOpener*/ nullptr, PlaceholderType::get, + /*unboundTyOpener*/ nullptr, /*placeholderOpener*/ nullptr, /*packElementOpener*/ nullptr); } @@ -7045,7 +7045,7 @@ Type ExplicitCaughtTypeRequest::evaluate( return TypeResolution::resolveContextualType( typeRepr, doCatch->getDeclContext(), TypeResolutionOptions(TypeResolverContext::None), - /*unboundTyOpener*/ nullptr, PlaceholderType::get, + /*unboundTyOpener*/ nullptr, /*placeholderOpener*/ nullptr, /*packElementOpener*/ nullptr); } diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index d753b454c1d..6a3282621c1 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -216,3 +216,8 @@ struct NotAnError {} func badThrowingFunctionType(_: () throws(NotAnError) -> ()) {} // expected-error@-1 {{thrown type 'NotAnError' does not conform to the 'Error' protocol}} + +struct GenericError: Error {} + +func placeholderThrowing1() throws(_) {} // expected-error {{type placeholder not allowed here}} +func placeholderThrowing2() throws(GenericError<_>) {} // expected-error {{type placeholder not allowed here}} diff --git a/test/expr/closure/typed_throws.swift b/test/expr/closure/typed_throws.swift index 26500dd2b2b..29b0e8d0b88 100644 --- a/test/expr/closure/typed_throws.swift +++ b/test/expr/closure/typed_throws.swift @@ -8,6 +8,8 @@ enum MyBadError { case fail } +struct GenericError: Error {} + func testClosures() { let c1 = { () throws(MyError) in throw .fail @@ -36,5 +38,8 @@ func testClosures() { let c2 = { throw MyError.fail } let _: Int = c2 // expected-error@-1{{cannot convert value of type '() throws -> ()'}} + + _ = { () throws(_) in } // expected-error {{type placeholder not allowed here}} + _ = { () throws(GenericError<_>) in } // expected-error {{type placeholder not allowed here}} } diff --git a/test/stmt/typed_throws.swift b/test/stmt/typed_throws.swift index 00451c0eedd..be7eeb39d50 100644 --- a/test/stmt/typed_throws.swift +++ b/test/stmt/typed_throws.swift @@ -12,6 +12,8 @@ case dogAteIt case forgot } +struct GenericError: Error {} + func processMyError(_: MyError) { } func doSomething() throws(MyError) { } @@ -418,3 +420,12 @@ func testSequenceExpr() async throws(Never) { try true ? 0 : try! getInt() ~~~ getInt() // expected-error@-1 {{'try!' following conditional operator does not cover everything to its right}} } + +func testPlaceholder() { + do throws(_) {} catch {} + // expected-error@-1 {{type placeholder not allowed here}} + // expected-warning@-2 {{'catch' block is unreachable because no errors are thrown in 'do' block}} + do throws(GenericError<_>) {} catch {} + // expected-error@-1 {{type placeholder not allowed here}} + // expected-warning@-2 {{'catch' block is unreachable because no errors are thrown in 'do' block}} +}