Segmentation fault using generics in sequence

I use custom generic Sequence which stores closures inside:

final class ArrayHolder<T>: Sequence {
    var arr = [() -> T?]()

    public func add(_ element: @escaping () -> T?) {
        arr.append(element)
    }

    public func makeIterator() -> Array<T>.Iterator {
        return arr.compactMap { $0() }.makeIterator()
    }
}

I also use generic function over T: AnyObject to create closure which returns weak object reference:

public func weakify<T: AnyObject>(_ object: T) -> (() -> T?) {
    { [weak object] in object }
}

And I get Segmentation Fault when I call Sequence.forEach method like this:

let c = MyClass()
let arrHolder = ArrayHolder<MyClass>()
arrHolder.add(weakify(c))

arrHolder.forEach { // Segmentation Fault
    print($0)
}

While trying to investigate reasons behind Segmentation Fault, I observed weird behaviour:

// new weakify function with logs
func weakify<T: AnyObject>(_ object: T) -> (() -> T?) {
        weak var obj: T? = object
        return {
            if obj == nil {
                print("weakify nil")
            } else {
                print("weakify non nil") // prints "weakify non nil"
            }        
            return obj
        }
    }

// modified call side

arrHolder.add(weakify(c))
let notNils = arrHolder.arr.compactMap {
    let res = $0()
    if let res {
        print("compact map not nil")
    } else {
        print("compact map nil") // prints "compact map nil"
    }
    return res
}
print(notNils.count, arrHolder.count) // prints 0, 1

Surely, some dark magic is hidden there.
I don't know what to do next.
Thanks in advance.

2 Likes

Recently we found interesting work-around:

let f = weakify(c)
arrHolder.add(f)

But it's still unclear, why inlining f leads to Segmentation Fault

Also, we found more compact sample to illustrate issue

final class MyClass { }

func weakify<T: AnyObject>(_ object: T) -> (() -> T) {
    { object }
}

func printer<T>(_ factory: @escaping () -> T) {
    print(factory())
}

let c = MyClass()
printer(weakify(c)) // Segmentation Fault

Both ways work here:

import Foundation

final class MyClass  {

}

func weakify<T: AnyObject>(_ object: T) -> (() -> T) {
	{ object }
}

func nonEscapingPrinter<T>(_ factory: () -> T) {
	print(factory())
}

func escapingPrinter<T>(_ factory: @escaping () -> T) {
	print(factory())
}

let c = MyClass()
nonEscapingPrinter(weakify(c))

let weakifyFunc = weakify(c)
escapingPrinter(weakifyFunc)

Not sure but using @escaping, where the function object is temporary, it is no longer there, the way you call it. When it is made to an object (let weakifyFunc = ...), it works since it is "alive" until the end of the scope. Maybe someone else knows better.

The first (big) fragment crashes for me in Debug (-Onone).
Looks like a bug.

Interesting that with the code in the original question this works:

// ...
let weakified = weakify(c)
arrHolder.add(weakified)
let d = MyClass()
arrHolder.add(weakify(d))

arrHolder.forEach { // No Segmentation Fault
	print($0)
}

And it prints out:

SwiftTestProject.MyClass
SwiftTestProject.MyClass
Program ended with exit code: 0

Edit: Perhaps because the weakify func is still in scope because of weakified?