swift - Compiler choosing the wrong initializer -


i have exc_bad_access(code=2 ...) error keeps popping need with. believe i've managed pin down source of malformed pointer, i'm @ loss how fix it.

apparently swift compiler choosing wrong initializer 1 of classes. according instruments, when class description initialized, calls initializer lyricblock. not time, sometimes. regardless of whether compiler set -onone or -o whole-module-optimization.

here's 2 classes like:

class lyricblock: node, leftdelimited, rightdelimited {      var leftdelimiter: delimiter      var rigthdelimiter: delimiter      init(start: int, body: range<int>, end: int) {         self.leftdelimiter = delimiter(range: start..<body.lowerbound)         self.rightdelimiter = delimiter(range: body.upperbound..<end)         super.init(range: body)     }  }  class description: node, leftdelimited, rightdelimited {      var leftdelimiter: delimiter      var leftdelimiter: delimiter      init(start: int, body: range<int>, end: int) {         self.leftdelimiter = delimiter(range: start..<body.lowerbound)         self.rightdelimiter = delimiter(range: body.upperbound..<end)         super.init(range: body)     }  } 

as can see, lyricblock , description inherit node base class , share couple protocols in common, otherwise have nothing each other.

some possibly relevant code:

class node {      weak var parent: node?      var next: node?      var firstchild: node?      weak var lastchild: node?      let offset: int      internal(set) var length: int      init(range: range<int>) {         self.offset = range.lowerbound         self.length = range.upperbound - range.lowerbound     }      func addchild(_ child: node) {         if firstchild == nil {             firstchild = child         } else {             lastchild?.next = child         }          lastchild = child         child.parent = self     }  }  class parser {      // ...      func processline(in buffer: buffer) {         // parse current line block node.         var block = blockforline(in: buffer)          // try find appropriate container node. if none can found, block replaced description.         let container = appropriatecontainer(for: &block, in: buffer)         container.addchild(block)          // edge case parse first-line lyrics         if let cueblock = block as? cueblock {             if let lyricblock = scanforlyric(in: buffer, at: cueblock.direction.range.lowerbound) {                 let lyriccontainer = lyriccontainer(range: lyricblock.range.lowerbound..<endoflinecharnumber)                 lyriccontainer.addchild(lyricblock)                  cueblock.replacedirection(with: lyriccontainer)                  parseinlines(for: lyricblock, in: buffer)             }         }          // parse inlines appropriate         switch block {         case facsimileblock, description, lyricblock:             parseinlines(for: block, in: buffer)         // ...         }     }      func blockforline(in buffer: buffer) -> node {         let whitespace = buffer.scanforfirstnonspace(at: charnumber, limit: endoflinecharnumber)          // ...           let endwhitespace = buffer.scanbackwardforfirstnonspace(at: endoflinecharnumber, limit: wc)          let description = description(start: charnumber, body: whitespace..< endwhitespace, end: endoflinecharnumber)         return description     }      func appropriatecontainer(for block: inout node, in buffer: buffer) -> node {         switch block {         // these block types can ever level-1         case header, description, endblock, horizontalbreak:             return root          // ...          case lyricblock:             guard let cuecontainer = root.lastchild as? cuecontainer else { break }              guard let cueblock = cuecontainer.lastchild as? cueblock else { break }              guard let direction = cueblock.direction as? lyriccontainer else { break }              direction.extendlengthtoinclude(node: block)             cueblock.extendlengthtoinclude(node: direction)             cuecontainer.extendlengthtoinclude(node: cueblock)             return direction          default:             break         }          let whitespace = buffer.scanforfirstnonspace(at: charnumber, limit: endoflinecharnumber)         let endwhitespace = buffer.scanbackwardforfirstnonspace(at: endoflinecharnumber, limit: wc)          // invalid syntax, time fail gracefully         block = description(start: charnumber, body: whitespace..< endwhitespace, end: endoflinecharnumber)         return root     }      func parseinlines(for stream: node, in buffer: buffer) {         // ... scans buffer inlines , enques them in queue          while let next = queue.dequeue() {             let nextrange = next.rangeincludingmarkers              if nextrange.lowerbound > j {                 let lit = literal(range: j..<nextrange.lowerbound)                 stream.addchild(lit)             }              stream.addchild(next)              j = nextrange.upperbound         }          if j < endoflinecharnumber {             let lit = literal(range: j..<noderange.upperbound)             stream.addchild(lit)         }     }      // ...  } 

as side note, wondered if might running mangling issue class signatures , tried making rightdelimiter , leftdelimiter properties of node instead of using protocols. resulted in compiler calling identifier initializer instead. don't know proves. frankly i'm @ loss. help?

after more tinkering able @ least partially solve problem.

it turns out that, whatever reason, arc craps out on linked lists above size. able confirm creating generic linked list in new project , stress testing progressively longer lists.

class llnode<t> {      var value: t?      var next: llnode<t>?      weak var previous: llnode<t>?  }  class llist<t> {      var head: llnode<t>      var tail: llnode<t>      // boring old linked list implementation  }  let list = list<int>() in 0..<10000 {     list.append(i) } 

sure enough, after 10,000 nodes started getting exc_bad_access again on generic list, when list deinitialized. matched behavior getting parser above. since code uses linked lists model children in tree (two dimensions of reference counting!), arc having resolve references on own -- , failing. why crashes still beyond ability explain, @ least explains source of crash.

to confirm, created linked list in c , made swift wrapper around it, of garbage collection being implemented in c.

import liblist  class list<t> {      var list: unsafemutablepointer<llist>      // swift interface list      deinit {         list_free(list)     } } 

the wrapper able handle every size threw @ -- as 500,000 nodes, @ point felt satisfied , stopped testing.

if interested in full code used around problem, i've created swift package @ https://github.com/dmcarth/list


Comments