Weird EXC_BAD_ACCESS in Swift 1.2 Was Due to A Compiler Bug

I banged my head against Xcode’s wall (which is my computer screen) today, trying to find the source of an EXC_BAD_ACCESS code=2 termination.

Turns out it’s the Swift 1.2 compiler which doesn’t catch a problem here.

typealias Factory = (value: Int) -> Thing
var createThingBlock: Factory = defaultCreateThingBlock

let defaultCreateThingBlock = {
    //(value: Int) -> Thing in  // too verbose for my taste
    //value -> Thing in         // enforced by Swift 2
    value in                    // compiles in Swift 1.2 but crashes

    return ConcreteThing(value: value)
}

The signature of the block causing trouble with the version shown above:

defaultCreateThingBlock: (Int) -> ConcreteThing

This works when the Thing protocol is for reference types only in Swift 1.2 – until it is used. Then the process crashes.

It seems (Int) -> ConcreteThing should not match (Int) -> Thing. According to the type inheritance tree this looks good enough, but it isn’t good enough for Swift.

Here follows the fully working crashing example Cocoa app. Remember the technique to [switch free function implementations during tests][test]?

protocol Thing: class {
    var x: Int { get }

    func foo()
}

class ConcreteThing: Thing {

    let x: Int

    init(value: Int) {
        x = value
    }

    func foo() {
        print("success! ==\(x)")
    }
}

func createThingWithValue(value: Int) -> Thing {
    return createThingBlock(value: value)
}

typealias Factory = (value: Int) -> Thing

var createThingBlock: Factory = defaultCreateThingBlock

let defaultCreateThingBlock = {
    //(value: Int) -> Thing in  // too verbose for my taste
    //value -> Thing in         // enforced by Swift 2
    value in                    // compiles in Swift 1.2 but crashes

    return ConcreteThing(value: value)
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    func applicationDidFinishLaunching(aNotification: NSNotification) {
    
        let thing = createThingWithValue(42)
        NSLog("\(thing)")
        NSLog("\(thing.x)")    // <-- crashes here
    
        NSApplication.sharedApplication().terminate(self)
    }
}

It will compile with Swift 1.2, but it won’t with Swift 2:

SIL verification failed: convert_function cannot change function ABI
  ABI-incompatible return values
  @callee_owned (Int) -> @owned ConcreteThing
  @callee_owned (Int) -> @owned Thing

Thank you for reporting this, dear compiler!

Switching back to Xcode 6 and Swift 1.2 afterwards without cleaning the build products all of a sudden reports a compiler error in Swift 1.2, too. So the compiler understands the problem but it doesn’t seem to have caught it.

So if your Swift 1.2 app crashes all of a sudden, look for weird type dances.