swiftswift6typed-throws

Exhaustive catch clauses for errors from typed-throws


Using typed-throws (SE-0413) in Swift 6.0 (Xcode 16.0), it can correctly detect typed errors. Consider:

enum MyError: Error {
    case one
    case two
}

func funcWithTypedThrow() async throws(MyError) {…}

If you use separate catch statements for those individual MyError cases, the compiler will incorrectly conclude that the catches are not exhaustive:

// An example where typed-throw correctly infers the type of the thrown error, but
// incorrectly concludes catch is not exhaustive.

func foo() async {
    do {
        try await funcWithTypedThrow() // Swift 6.0 compiler incorrectly produces error: “Errors thrown from here are not handled because the enclosing catch is not exhaustive”
    } catch .one {
        print("one")
    } catch .two {
        print("two")
    }
}

But if I add an extra catch to silence that error, the compiler will realize that this is unnecessary and will warn that it “will never be executed”:

// In this example, I added extra `catch` to silence the above error, but now 
// the compiler now warns us that that this extra `catch` is unnecessary.

func bar() async {
    do {
        try await funcWithTypedThrow()
    } catch .one {
        print("one")
    } catch .two {
        print("two")
    } catch {  // Swift 6.0 compiler correctly produces warning: “Case will never be executed”
        print("this is required to silence 'non-exhaustive' error, but generates a 'will never be executed' warning")
    }
}

How can I get around this issue?


Solution

  • One can just catch the typed-error and then use a switch statement:

    func qux() async {
        do {
            try await funcWithTypedThrow()
        } catch {
            switch error {
                case .one: print("one")
                case .two: print("two")
            }
        }
    }
    

    This seems to avoid the incorrect “exhaustiveness” checks when using separate catch clauses.