google/promises

Generic initialiser type mismatch causes crashes

tiarnann opened this issue · 1 comments

Problem

The following code compiles and causes a crash

let somePromise = Promise<String> {
     return Optional<Void>.none
}

somePromise.then { (v: String) in
  // causes crash
  print(v)
}

Explanation

Here is a piece of code that causes this crash. The naming of the initialiser generic type Value can be easily confused with the class generic Value type, so I'll use two different names for my explanation below.

convenience init<Value>(on queue: DispatchQueue = .promises, _ work: @escaping Do<Value>) {
let objCPromise = ObjCPromise<AnyObject>.__onQueue(queue) {
do {
let resolution = try work()
return resolution as? NSError ?? Promise<Value>.asAnyObject(resolution)
} catch let error {
return error as NSError
}
}
self.init(objCPromise)
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(self)
}

The way the current convenience initialisers in Promise+Do.swift are written, they allow the user to create a promise of some type OneValue and pass a function that returns a type SecondValue. This is because the convenience initialisers contain their own generic type SecondValue which does not have the match the OneValue type of the class Promise. Since the generic type is erased to ObjCPromise<AnyObject> when any class methods are run that cast the resolved to the class OneValue type

class Promise<OneValue>
  convenience init<SecondValue>(on queue: DispatchQueue = .promises, _ work: @escaping Do<SecondValue) {
    let objCPromise = ObjCPromise<AnyObject>.__onQueue(queue) {
      /*....*/
    }
    self.init(objCPromise)
    objCPromise.__addPendingObject(self)
 }

Is there any reason why there are generic initialiser types used in the project like these?

convenience init<Value>(on queue: DispatchQueue = .promises, _ work: @escaping Do<Value>) {

convenience init<Value>(