Pitch: `rethrows(unchecked)`

For what it's worth, I'm running into the higher-order function problem, implementing type erasure. Reduced example:

protocol P {
  associatedtype A
  func invoke(_ body: () throws->()) rethrows
}

/// Type-eraser with nonthrowing closure.
struct AnyP0 {
  struct A {}
  let value: Any
  let invoker: (Self, _ body: () throws->()) -> Void

  init<T: P>(_ x: T) {
    value = x
    invoker = { self_, body in try! (self_.value as! T).invoke(body) }
  }
}

extension AnyP0: P {
  /// - WARNING: This signature lies and claims it supports throwing functions,
  ///   but it does not; if `body` throws, this method will trap!
  func invoke(_ body: () throws->()) rethrows {
    invoker(self, body)
  }
}

/// Type-eraser with throwing closure.
struct AnyP1 {
  struct A {}
  let value: Any
  let invoker: (Self, _ body: () throws->()) throws -> Void

  init<T: P>(_ x: T) {
    value = x
    invoker = { self_, body in try (self_.value as! T).invoke(body) }
  }
}

extension AnyP1: P {
  /// - WARNING: This signature lies; all errors thrown by body will be
  ///   swallowed.  
  func invoke(_ body: () throws->()) rethrows {
    do {
      try invoker(self, body)
    }
    catch let e {
      // throw e <-- error: a function declared 'rethrows' may only throw if its parameter does
      _ = e // only choice is to swallow the error.
    }
  }
}

In my case, it might be possible to solve the problem with rethrows as a first-class type annotation, but I don't think all cases will work that way, so there should be an escape clause in the library to deal with the places where the knowledge is really only available to the programmer.

1 Like