SIL: Fix bug in AbstractionPattern::getFunctionThrownErrorType()

When storing a closure with type `() throws(any Error) -> ()` as
a fully opaque error, we want to leave it unchanged, instead of
re-abstracting it to throw the error indirectly. Thus, we had
a special carveout here.

However, the carveout was too broad, because if the thrown error
type contained type parameters, the resulting AbstractionPattern
was invalid.

While fixing this I realized this entire hack is unsound in some
cases, if you view the same value as a `() throws(U) -> ()` vs
an `() -> throws(any Error) -> ()`.

Perhaps we should always box the thrown error when maximally
abstracting a closure, but that would also be an ABI break.

- Fixes https://github.com/swiftlang/swift/issues/84051
This commit is contained in:
Slava Pestov
2025-09-09 19:25:11 -04:00
parent 60246a2889
commit 375212db9e
2 changed files with 16 additions and 1 deletions

View File

@@ -1366,7 +1366,14 @@ AbstractionPattern::getFunctionThrownErrorType(
if (!substErrorType)
return std::nullopt;
return std::make_pair(AbstractionPattern(*substErrorType),
// FIXME: This is actually unsound. The most opaque form of
// `(T) throws(U) -> V` should actually be
// `(T) throws(any Error) -> V`.
auto pattern = ((*substErrorType)->isErrorExistentialType()
? AbstractionPattern(*substErrorType)
: AbstractionPattern::getOpaque());
return std::make_pair(pattern,
(*substErrorType)->getCanonicalType());
}

View File

@@ -0,0 +1,8 @@
// RUN: %target-swift-emit-silgen %s
// RUN: %target-swift-emit-silgen %s -enable-library-evolution
// RUN: %target-swift-emit-silgen %s -enable-testing
// RUN: %target-swift-emit-silgen %s -enable-library-evolution -enable-testing
public struct Visitor<Node, Failure: Error> {
public var visit: (Node) throws(Failure) -> Void
}