Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does iterating over closures cause a bus error in swift?

Tags:

swift

I'm getting a strange Bus Error when running what appears to be perfectly safe swift code. I've tried to reduce it down to a minimal test case, as follows:

Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 0ddf238ad7)
Target: x86_64-apple-macosx10.9

This code:

public enum MyError: ErrorType {
  case SomeError(code: Int)
}

public typealias MyType = () throws -> Bool

public class Foo {
  var a:MyType = { () throws -> Bool in
    print("A")
    return true
  }
  var b:MyType = { () throws -> Bool in
    print("B")
    return true
  }
  var c:MyType = { () throws -> Bool in
    print("C")
    throw MyError.SomeError(0)
  }
}

public func handle<T>(test:T) {
  let mirror = Mirror(reflecting: test)
  print(mirror.subjectType)
  for child in mirror.children {
    if let callable = child.value as? MyType {
      do {
        try callable()
      }
      catch MyError.SomeError(let id) {
        print(id)
      }
      catch {
        print("unknown error")
      }
    }
  }
}

let foo = Foo()
handle(foo)

Generates this output:

Foo
A
B
C
Bus error: 10

Running it in the debugger works fine, so I assume it has something to do with a timing issue at runtime.

Am I doing something illegal or unsafe in this code?

Are exceptions somehow illegal in closures?

What's causing this error?

Edit:

I've created a bug on the swift issue tracker for this now here: https://bugs.swift.org/browse/SR-324

like image 349
Doug Avatar asked Dec 08 '15 02:12

Doug


2 Answers

What's causing this error?

The error doesn't happen until you get to the last closure:

var c:MyType = { () throws -> Bool in
    print("C")
    throw MyError.SomeError(0)
}

Obviously, you're throwing an exception here, and I suspect that the problem has less to do with iterating over the children and more to do with throwing an exception while you're doing that iterating. I tried calling c without iterating:

public func trythis() {
    let foo = Foo()
    do {
        try (foo.c)()
    }
    catch MyError.SomeError(let id) {
        print(id)
    }
    catch { print("unknown") }
}

trythis()

and found that it worked fine. I also tried removing the throw from c:

var c:MyType = { () throws -> Bool in
    print("C")
//  throw MyError.SomeError(code: 0)
    return true
}

and found that the code works fine in that case. So it's the combination of throwing while iterating over the list that's the problem, and that makes me suspect that it's just a compiler bug or maybe some problem with the Mirror class.

I think you should file a bug report with Apple for this one.

like image 196
Caleb Avatar answered Oct 19 '22 20:10

Caleb


I agree with Caleb that this must be a bug.

But just to be clear, it is not the combination of throwing while iterating. It is the combination of reflecting and throwing.

This is a modified version of your handle function:

public func handle<T>(test:T) {
  let mirror = Mirror(reflecting: test)
  print(mirror.subjectType)

  // Extract only the last function, no iteration...
  if let callable = mirror.children[AnyForwardIndex(2)].value as? MyType {
    do {
      try callable()
    }
    catch MyError.SomeError(let id) {
      print(id)
    }
    catch {
      print("unknown error")
    }

  }
}

This function will cause the same error as your function. You simply can not call a function that throws, if found using reflection.

Bug I would say.

like image 45
Mikael Hellman Avatar answered Oct 19 '22 21:10

Mikael Hellman