From 375212db9e595db78888c8ab505293566dda8f74 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 9 Sep 2025 19:25:11 -0400 Subject: [PATCH] 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 --- lib/SIL/IR/AbstractionPattern.cpp | 9 ++++++++- test/SILGen/keypath_typed_throws.swift | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/SILGen/keypath_typed_throws.swift diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 59fe0da628ed0..e7913e3b735ca 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -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()); } diff --git a/test/SILGen/keypath_typed_throws.swift b/test/SILGen/keypath_typed_throws.swift new file mode 100644 index 0000000000000..4fc9e02be99b5 --- /dev/null +++ b/test/SILGen/keypath_typed_throws.swift @@ -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 { + public var visit: (Node) throws(Failure) -> Void +}