Uploaded image for project: 'Swift'
  1. Swift
  2. SR-1408

NSArray indexesOfObjectsPassingTest cannot be called in Swift 3

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: Compiler
    • Labels:
    • Environment:

      Xcode 7.3.1 and any recent Swift 3 toolchain

      Description

      The following code doesn't compile in any recent toolchain, due to issue with renamification and the rules for Swift default parameters:

                  let pep = ["Manny", "Moe", "Jack"] as NSArray
                  let ixs : NSIndexSet = pep.indexesOfObjects(passingTest: {
                      (obj:AnyObject, ix:Int, stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
                      return true
                  })
      

      Basically it is now unnecessarily difficult to call NSArray indexesOfObjects. The reason is that Swift sees the passingTest: parameter as ambiguous. Now, you might say: huh? Ambiguous with what? Well, it's rather subtle. Here's the detailed complaint from the compiler:

      Foundation.NSArray:77:17: note: found this candidate
      
          public func indexesOfObjects(passingTest predicate: (AnyObject, Int, UnsafeMutablePointer<ObjCBool>) -> Bool) -> NSIndexSet
      
      Foundation.NSArray:80:17: note: found this candidate
      
          public func indexesOfObjects(_ opts: NSEnumerationOptions = [], passingTest predicate: (AnyObject, Int, UnsafeMutablePointer<ObjCBool>) -> Bool) -> NSIndexSet
      

      The nameless parameter in the second method has a default value, and both methods have a `passingTest` parameter, so in the absence of further information, our code could now be calling either of them.

      This analysis suggests (rightly) that we can worm our way out of the difficulty by saying:

                  let ixs : NSIndexSet = pep.indexesOfObjects([], passingTest: {
                      (obj:AnyObject, ix:Int, stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
                      return true
                  })
      

      Yecch! What is the point of having an optional parameter (a parameter with a default value) if you have to supply it anyway? How did we get ourselves into this mess in the first place?

      The answer is that Objective-C has two perfectly separate methods:

          indexesOfObjectsWithOptions:...
      

      and

          indexesOfObjectsPassingTest:...
      

      A moment's thought will reveal that Swift renamification has conflated these two methods into one. It is therefore impossible to call indexesOfObjectsPassingTest — it is effectively stricken from the field. Only indexesOfObjectsWithOptions can be called.

      This could easily be resolved by giving the options parameter an explicit label. We would then have indexesOfObjects(options:... ) and indexesOfObjects(passingTest:... ) and all would be well.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned
              Reporter:
              mattneub Matt Neuburg
              Votes:
              1 Vote for this issue
              Watchers:
              4 Start watching this issue

                Dates

                Created:
                Updated: