google / promises

Promises is a modern framework that provides a synchronization construct for Swift and Objective-C.
Apache License 2.0
3.79k stars 292 forks source link

Generic initialiser type mismatch causes crashes #177

Open tiarnann opened 2 years ago

tiarnann commented 2 years ago

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.

https://github.com/google/promises/blob/afa9a1ace74e116848d4f743599ab83e584ff8cb/Sources/Promises/Promise%2BDo.swift#L26-L38

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?

https://github.com/google/promises/blob/afa9a1ace74e116848d4f743599ab83e584ff8cb/Sources/Promises/Promise%2BDo.swift#L26

https://github.com/google/promises/blob/afa9a1ace74e116848d4f743599ab83e584ff8cb/Sources/Promises/Promise%2BDo.swift#L45