Is throws() type-checking really this limited?

I'm trying out typed throws, and quickly ran into this problem (Swift 6.3.1)

struct ErrorA: Error { }
struct ErrorB: Error { }

func onlyA() throws(ErrorA) {
    do {
        throw ErrorB()
    } catch is ErrorB {
        throw ErrorA()
    }   // ERROR: Thrown expression type 'any Error' cannot be converted to error type 'ErrorA'
}

The compiler doesn't seem to realize that the do block only throws ErrorB. This doesn't work either:

func onlyA() throws(ErrorA) {
    do {
        throw ErrorB()
    } catch is ErrorB {
        throw ErrorA()
    } catch let error as ErrorA {
        throw error
    } // Thrown expression type 'any Error' cannot be converted to error type 'ErrorA'
}

The only fix seems to be to append catch {throw ErrorA()} to convince the compiler that no unknown errors can sneak past.

Obviously this is a contrived example with a simple workaround. In my real function, the do block is more complex and can throw either ErrorA or ErrorB; I only want to catch ErrorB, and allow a thrown ErrorA past unaltered because it has a specific error message.

Yes, there's essentially no inference for typed throws until we get FullTypedThrows back. You can work around this by using throws(ErrorType) in various ways, such as do throws(ErrorType) which should get you want you want here.

If you're throwing multiple error types, you either need to use a unifying type to use typed throws, or do the typical catch let error as X and ignore the required catch clause.

2 Likes

Thanks for the help. After I got my code building, I found that compiling the unit tests now crashes the compiler; I filed a bug report. I'll probably back this whole change out.