[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.
This commit is contained in:
Hamish Knight
2025-09-17 15:23:08 +01:00
parent 71f8e68655
commit 9f5a754b77
5 changed files with 29 additions and 5 deletions

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -216,3 +216,8 @@ struct NotAnError<T> {}
func badThrowingFunctionType<T>(_: () throws(NotAnError<T>) -> ()) {}
// expected-error@-1 {{thrown type 'NotAnError<T>' does not conform to the 'Error' protocol}}
struct GenericError<T>: Error {}
func placeholderThrowing1() throws(_) {} // expected-error {{type placeholder not allowed here}}
func placeholderThrowing2() throws(GenericError<_>) {} // expected-error {{type placeholder not allowed here}}

View File

@@ -8,6 +8,8 @@ enum MyBadError {
case fail
}
struct GenericError<T>: 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}}
}

View File

@@ -12,6 +12,8 @@ case dogAteIt
case forgot
}
struct GenericError<T>: 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}}
}