Call to rethrow functions inside typed throw

Compiler cannot infer rethrow error type when calling rethrows function inside throws(e)

The rethrows telling us “Error type thrown is the same passed closure throws”.
But in this simplified example in some reason it cannot infer that body error type is exact, not any Error.

What I’m missing?
Are there suggestions on using rethrows functions (like DispatchQueue.sync) inside typed-throw ones.

func rethrowFn(_ body: () throws -> Void) rethrows {
    try body()
}

func typedThrowFn<E: Error>(_ body: () throws(E) -> Void) throws(E) {
    // Error: Thrown expression type 'any Error' cannot be converted to error type 'E'
    try rethrowFn {
        try body()
    }
}
func rethrowFn(_ body: () throws -> Void) rethrows {
    try body()
}
func rethrowFn<E: Error>(_ body: () throws(E) -> Void) throws(E) {
    try body()
}

If E is inferred to be Never, you don't need try at call site, in effect making both of them non-throwing, when passing a non-throwing argument. Is this not working for you?

func rethrowFn<E: Error>(_ body: () throws(E) -> Void) throws(E) {
    try body()
}

func typedThrowFn<E: Error>(_ body: () throws(E) -> Void) throws(E) {
    try rethrowFn(body)
}
func untypedThrowFn(_ body: () throws -> Void) rethrows {
    try rethrowFn(body)
}
func nonThrowingFn(_ body: () -> Void) {
    rethrowFn(body)
    // ^^ no try
}
func nonThrowingCallsRethrowsFn(_ body: () -> Void) {
    untypedThrowFn(body)
    // ^ note this one is rethrows
}
2 Likes

I believe this is because rethrows doesn’t technically require that the error thrown is the same error the closure threw, just that the error is not thrown if the closure doesn’t throw. In other words, this is legal:

func f(cb: () throws -> Void) rethrows {
  do {
    try cb()
  } catch {
    throw SomeOtherError()
  }
}

If I know this isn’t what the function does, I often wrap it like so:

do {
  try someThrowingFunc()
} catch {
  throw error as! E
}
3 Likes

Strictly speaking, this can be spelled:

func f<E>(
  _ f1: () throws(E) -> Void,
  _ f2: () throws(E) -> Void
) throws(E) {
  try f1()
  try f2()
}

func f<E1, E2>(
  _ f1: () throws(E1) -> Void,
  _ f2: () throws(E2) -> Void
) throws(any Error) {
  try f1()
  try f2()
}