5

This one has me stumped. I can't figure out why Swift is complaining that self.init is called more than once in this code:

public init(body: String) {
    let parser = Gravl.Parser()

    if let node = parser.parse(body) {
        super.init(document: self, gravlNode: node)
    } else {
        // Swift complains with the mentioned error on this line (it doesn't matter what "whatever" is):
        super.init(document: self, gravlNode: whatever)
    }
}

Unless I'm missing something, it's very obvious that it is only calling init once. Funnily enough if I comment out the second line Swift complains that Super.init isn't called on all paths, lol.

What am I missing?

Update:

Ok so the problem was definitely trying to pass self in the call to super.init. I totally forgot I was doing that, ha. I think I had written that experimentally and gotten it to compile and thought that it might actually work, but looks like it's actually a bug that it compiled that way at all.

Anyhow, since passing self to an initializer is kind of redundant since it's the same object, I changed the parent initializer to accept an optional document parameter (it's just an internal initializer so no big deal) and if it's nil I just set it to self in the parent initializer.

For those curious, this is what the parent initializer (now) looks like:

internal init(document: Document?, gravlNode: Gravl.Node) {
    self.value = gravlNode.value
    super.init()
    self.document = document ?? self as! Document
    // other stuff...
}
devios1
  • 36,899
  • 45
  • 162
  • 260

1 Answers1

3

I suspect this is a bad diagnostic (i.e the wrong error message). It would be very helpful if you had a full example we could experiment with, but this line doesn't make sense (and I suspect is the underlying problem):

    super.init(document: self, gravlNode: node)

You can't pass self to super.init. You're not initialized yet (you're not initialized until you've called super.init). For example, consider the following simplified code:

class S {
    init(document: AnyObject) {}
}

class C: S {
    public init() {
        super.init(document: self)
    }
}

This leads to error: 'self' used before super.init call which I believe is the correct error.

EDIT: I believe Hamish has definitely uncovered a compiler bug. You can exploit it this way in Xcode 8.3.1 (haven't tested on 8.3.2 yet):

class S {
    var x: Int
    init(document: S) {
        self.x = document.x
    }
}

class C: S {
    public init() {
        super.init(document: self)
    }
}

let c = C() // malloc: *** error for object 0x600000031244: Invalid pointer dequeued from free list
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    What's interesting is if the `document:` parameter is typed as `S`, the compiler accepts it... which is worrying. It has been [filed as a bug here](https://bugs.swift.org/browse/SR-607). – Hamish Apr 29 '17 at 14:49
  • Yeah you're right that must be it. That was a little trickery I had done earlier. The strange thing is I swear it was working before. – devios1 Apr 29 '17 at 14:51
  • The same issue happens on 8.3.2 once you attempt to use `c`. – Sulthan Apr 29 '17 at 14:58