# # # The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim # -------------------------- constant expressions ------------------------ proc int64Literal(i: BiggestInt): Rope = if i > low(int64): result = "IL64($1)" % [rope(i)] else: result = ~"(IL64(-9223372036854775807) - IL64(1))" proc uint64Literal(i: uint64): Rope = rope($i & "ULL") proc intLiteral(i: BiggestInt): Rope = if i > low(int32) and i <= high(int32): result = rope(i) elif i == low(int32): # Nim has the same bug for the same reasons :-) result = ~"(-2147483647 -1)" elif i > low(int64): result = "IL64($1)" % [rope(i)] else: result = ~"(IL64(-9223372036854775807) - IL64(1))" proc intLiteral(i: Int128): Rope = intLiteral(toInt64(i)) proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = case n.kind of nkCharLit..nkUInt64Lit: var k: TTypeKind if ty != nil: k = skipTypes(ty, abstractVarRange).kind else: case n.kind of nkCharLit: k = tyChar of nkUInt64Lit: k = tyUInt64 of nkInt64Lit: k = tyInt64 else: k = tyNil # don't go into the case variant that uses 'ty' case k of tyChar, tyNil: result = intLiteral(n.intVal) of tyBool: if n.intVal != 0: result = ~"NIM_TRUE" else: result = ~"NIM_FALSE" of tyInt64: result = int64Literal(n.intVal) of tyUInt64: result = uint64Literal(uint64(n.intVal)) else: result = "(($1) $2)" % [getTypeDesc(p.module, ty), intLiteral(n.intVal)] of nkNilLit: let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", [getTypeDesc(p.module, ty), result]) else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: let k = if ty == nil: tyString else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind case k of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: # with the new semantics for 'nil' strings, we can map "" to nil and # save tons of allocations: if n.strVal.len == 0 and optNilSeqs notin p.options and p.config.selectedGC != gcDestructors: result = genNilStringLiteral(p.module, n.info) else: result = genStringLiteral(p.module, n) else: result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: result = rope(n.floatVal.toStrMaxPrecision("f")) else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') result = nil proc genLiteral(p: BProc, n: PNode): Rope = result = genLiteral(p, n, n.typ) proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = result = 0 for j in 0 ..< size: if j < len(s): result = result or (BiggestUInt(s[j]) shl (j * 8)) proc genRawSetData(cs: TBitSet, size: int): Rope = if size > 8: var res = "{\n" for i in 0 ..< size: res.add "0x" res.add "0123456789abcdef"[cs[i] div 16] res.add "0123456789abcdef"[cs[i] mod 16] if i < size - 1: # not last iteration if i mod 8 == 7: res.add ",\n" else: res.add ", " else: res.add "}\n" result = rope(res) else: result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size))) proc genSetNode(p: BProc, n: PNode): Rope = var cs: TBitSet var size = int(getSize(p.config, n.typ)) toBitSet(p.config, n, cs) if size > 8: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) else: result = genRawSetData(cs, size) proc getStorageLoc(n: PNode): TStorageLoc = case n.kind of nkSym: case n.sym.kind of skParam, skTemp: result = OnStack of skVar, skForVar, skResult, skLet: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnStack of skConst: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnUnknown else: result = OnUnknown of nkDerefExpr, nkHiddenDeref: case n.sons[0].typ.kind of tyVar, tyLent: result = OnUnknown of tyPtr: result = OnStack of tyRef: result = OnHeap else: doAssert(false, "getStorageLoc") of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv: result = getStorageLoc(n.sons[0]) else: result = OnUnknown proc canMove(p: BProc, n: PNode; dest: TLoc): bool = # for now we're conservative here: if n.kind == nkBracket: # This needs to be kept consistent with 'const' seq code # generation! if not isDeepConstExpr(n) or n.len == 0: if skipTypes(n.typ, abstractVarRange).kind == tySequence: return true elif optNilSeqs notin p.options and n.kind in nkStrKinds and n.strVal.len == 0: # Empty strings are codegen'd as NIM_NIL so it's just a pointer copy return true result = n.kind in nkCallKinds #if not result and dest.k == locTemp: # return true #if result: # echo n.info, " optimized ", n # result = false proc genRefAssign(p: BProc, dest, src: TLoc) = if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) elif dest.storage == OnHeap: linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), rdLoc(src)]) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), rdLoc(src)]) proc asgnComplexity(n: PNode): int = if n != nil: case n.kind of nkSym: result = 1 of nkRecCase: # 'case objects' are too difficult to inline their assignment operation: result = 100 of nkRecList: for t in items(n): result += asgnComplexity(t) else: discard proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != nil result.k = locField result.storage = a.storage result.lode = lodeTyp t result.r = rdLoc(a) & "." & field proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = if src.storage == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} else: flags let t = skipTypes(dest.t, abstractInst).getUniqueType() for i in 0 ..< t.len: let t = t.sons[i] let field = "Field$1" % [i.rope] genAssignment(p, optAsgnLoc(dest, t, field), optAsgnLoc(src, t, field), newflags) proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, t: PNode, typ: PType) = if t == nil: return let newflags = if src.storage == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} else: flags case t.kind of nkSym: let field = t.sym if field.loc.r == nil: fillObjectFields(p.module, typ) genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), optAsgnLoc(src, field.typ, field.loc.r), newflags) of nkRecList: for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ) else: discard proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # Consider: # type TMyFastString {.shallow.} = string # Due to the implementation of pragmas this would end up to set the # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so # (for objects, etc.): if p.config.selectedGC == gcDestructors: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", [addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)]) else: linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) else: linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # This function replaces all other methods for generating # the assignment operation in C. if src.t != nil and src.t.kind == tyPtr: # little HACK to support the new 'var T' as return type: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) return let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic}) case ty.kind of tyRef: genRefAssign(p, dest, src) of tySequence: if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): genRefAssign(p, dest, src) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", [addrLoc(p.config, dest), rdLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)]) of tyString: if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): genRefAssign(p, dest, src) else: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: var tmp: TLoc getTemp(p, ty, tmp) linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", [dest.rdLoc, src.rdLoc, tmp.rdLoc]) linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", [addrLoc(p.config, dest), rdLoc(src)]) of tyProc: if containsGarbageCollectedRef(dest.t): # optimize closure assignment: let a = optAsgnLoc(dest, dest.t, "ClE_0".rope) let b = optAsgnLoc(src, dest.t, "ClE_0".rope) genRefAssign(p, a, b) linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", [rdLoc(dest), rdLoc(src)]) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) of tyTuple: if containsGarbageCollectedRef(dest.t): if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags) else: genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) of tyObject: # XXX: check for subtyping? if ty.isImportedCppType: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) elif not isObjLackingTypeField(ty): genGenericAsgn(p, dest, src, flags) elif containsGarbageCollectedRef(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: discard getTypeDesc(p.module, ty) internalAssert p.config, ty.n != nil genOptAsgnObject(p, dest, src, flags, ty.n, ty) else: genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) of tyArray: if containsGarbageCollectedRef(dest.t): genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", [rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t)]) of tyOpenArray, tyVarargs: # open arrays are always on the stack - really? What if a sequence is # passed to an open array? if containsGarbageCollectedRef(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) else: linefmt(p, cpsStmts,
#
#
#            Nim's Runtime Library
#        (c) Copyright 2018 Nim contributors
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements an algorithm to compute the
## `edit distance`:idx: between two Unicode strings.

import unicode

proc editDistance*(a, b: string): int {.noSideEffect.} =
  ## Returns the unicode-rune edit distance between ``a`` and ``b``.
  ##
  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
  ## memory overhead.
  if len(a) > len(b):
    # make ``b`` the longer string
    return editDistance(b, a)
  # strip common prefix
  var
    i_start = 0 ## The character starting index of the first rune in both strings ``a`` and ``b``
    i_next_a = 0
    i_next_b = 0
    rune_a, rune_b: Rune
    len_runes_a = 0 ## The number of relevant runes in string ``a``.
    len_runes_b = 0 ## The number of relevant runes in string ``b``.
  block commonPrefix:
    # ``a`` is the shorter string
    while i_start < len(a):
      i_next_a = i_start
      a.fastRuneAt(i_next_a, rune_a, doInc = true)
      i_next_b = i_start
      b.fastRuneAt(i_next_b, rune_b, doInc = true)
      if rune_a != rune_b:
        inc(len_runes_a)
        inc(len_runes_b)
        break
      i_start = i_next_a
  var
    # we know that we are either at the start of the strings
    # or that the current value of rune_a is not equal to rune_b
    # => start search for common suffix after the current rune (``i_next_*``)
    i_end_a = i_next_a ## The exclusive upper index bound of string ``a``.
    i_end_b = i_next_b ## The exclusive upper index bound of string ``b``.
    i_current_a = i_next_a
    i_current_b = i_next_b
  block commonSuffix:
    var
      add_runes_a = 0
      add_runes_b = 0
    while i_current_a < len(a) and i_current_b < len(b):
      i_next_a = i_current_a
      a.fastRuneAt(i_next_a, rune_a)
      i_next_b = i_current_b
      b.fastRuneAt(i_next_b, rune_b)
      inc(add_runes_a)
      inc(add_runes_b)
      if rune_a != rune_b:
        i_end_a = i_next_a
        i_end_b = i_next_b
        inc(len_runes_a, add_runes_a)
        inc(len_runes_b, add_runes_b)
        add_runes_a = 0
        add_runes_b = 0
      i_current_a = i_next_a
      i_current_b = i_next_b
    if i_current_a >= len(a): # ``a`` exhausted
      if i_current_b < len(b): # ``b`` not exhausted
        i_end_a = i_current_a
        i_end_b = i_current_b
        inc(len_runes_a, add_runes_a)
        inc(len_runes_b, add_runes_b)
        while true:
          b.fastRuneAt(i_end_b, rune_b)
          inc(len_runes_b)
          if i_end_b >= len(b): break
    elif i_current_b >= len(b): # ``b`` exhausted and ``a`` not exhausted
      i_end_a = i_current_a
      i_end_b = i_current_b
      inc(len_runes_a, add_runes_a)
      inc(len_runes_b, add_runes_b)
      while true:
        a.fastRuneAt(i_end_a, rune_a)
        inc(len_runes_a)
        if i_end_a >= len(a): break
  block specialCases:
    # trivial cases:
    if len_runes_a == 0: return len_runes_b
    if len_runes_b == 0: return len_runes_a
    # another special case:
    if len_runes_a == 1:
      a.fastRuneAt(i_start, rune_a, doInc = false)
      var i_current_b = i_start
      while i_current_b < i_end_b:
        b.fastRuneAt(i_current_b, rune_b, doInc = true)
        if rune_a == rune_b: return len_runes_b - 1
      return len_runes_b
  # common case:
  var
    len1 = len_runes_a + 1
    len2 = len_runes_b + 1
    row: seq[int]
  let half = len_runes_a div 2
  newSeq(row, len2)
  var e = i_start + len2 - 1 # end marker
  # initialize first row:
  for i in 1 .. (len2 - half - 1): row[i] = i
  row[0] = len1 - half - 1
  i_current_a = i_start
  var
    char2p_i = -1
    char2p_prev: int
  for i in 1 .. (len1 - 1):
    i_next_a = i_current_a
    a.fastRuneAt(i_next_a, rune_a)
    var
      char2p: int
      D, x: int
      p: int
    if i >= (len1 - half):
      # skip the upper triangle:
      let offset = i + half - len1
      if char2p_i == i:
        b.fastRuneAt(char2p_prev, rune_b)
        char2p = char2p_prev
        char2p_i = i + 1
      else:
        char2p = i_start
        for j in 0 ..< offset:
          rune_b = b.runeAt(char2p)
          inc(char2p, rune_b.size)
        char2p_i = i + 1
        char2p_prev = char2p
      p = offset
      rune_b = b.runeAt(char2p)
      var c3 = row[p] + (if rune_a != rune_b: 1 else: 0)
      inc(char2p, rune_b.size)
      inc(p)
      x = row[p] + 1
      D = x
      if x > c3: x = c3
      row[p] = x
      inc(p)
    else:
      p = 1
      char2p = i_start
      D = i
      x = i
    if i <= (half + 1):
      # skip the lower triangle:
      e = len2 + i - half - 2
    # main:
    while p <= e:
      dec(D)
      rune_b = b.runeAt(char2p)
      var c3 = D + (if rune_a != rune_b: 1 else: 0)
      inc(char2p, rune_b.size)
      inc(x)
      if x > c3: x = c3
      D = row[p] + 1
      if x > D: x = D
      row[p] = x
      inc(p)
    # lower triangle sentinel:
    if i <= half:
      dec(D)
      rune_b = b.runeAt(char2p)
      var c3 = D + (if rune_a != rune_b: 1 else: 0)
      inc(x)
      if x > c3: x = c3
      row[p] = x
    i_current_a = i_next_a
  result = row[e]

proc editDistanceAscii*(a, b: string): int {.noSideEffect.} =
  ## Returns the edit distance between `a` and `b`.
  ##
  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
  ## memory overhead.
  var len1 = a.len
  var len2 = b.len
  if len1 > len2:
    # make `b` the longer string
    return editDistanceAscii(b, a)

  # strip common prefix:
  var s = 0
  while s < len1 and a[s] == b[s]:
    inc(s)
    dec(len1)
    dec(len2)
  # strip common suffix:
  while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
    dec(len1)
    dec(len2)
  # trivial cases:
  if len1 == 0: return len2
  if len2 == 0: return len1

  # another special case:
  if len1 == 1:
    for j in s..s+len2-1:
      if a[s] == b[j]: return len2 - 1
    return len2

  inc(len1)
  inc(len2)
  var half = len1 shr 1
  # initalize first row:
  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
  var row: seq[int]
  newSeq(row, len2)
  var e = s + len2 - 1 # end marker
  for i in 1..len2 - half - 1: row[i] = i
  row[0] = len1 - half - 1
  for i in 1 .. len1 - 1:
    var char1 = a[i + s - 1]
    var char2p: int
    var D, x: int
    var p: int
    if i >= len1 - half:
      # skip the upper triangle:
      var offset = i - len1 + half
      char2p = offset
      p = offset
      var c3 = row[p] + ord(char1 != b[s + char2p])
      inc(p)
      inc(char2p)
      x = row[p] + 1
      D = x
      if x > c3: x = c3
      row[p] = x
      inc(p)
    else:
      p = 1
      char2p = 0
      D = i
      x = i
    if i <= half + 1:
      # skip the lower triangle:
      e = len2 + i - half - 2
    # main:
    while p <= e:
      dec(D)
      var c3 = D + ord(char1 != b[char2p + s])
      inc(char2p)
      inc(x)
      if x > c3: x = c3
      D = row[p] + 1
      if x > D: x = D
      row[p] = x
      inc(p)
    # lower triangle sentinel:
    if i <= half:
      dec(D)
      var c3 = D + ord(char1 != b[char2p + s])
      inc(x)
      if x > c3: x = c3
      row[p] = x
  result = row[e]


when isMainModule:
  doAssert editDistance("", "") == 0
  doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia
  doAssert editDistance("flaw", "lawn") == 2 # from Wikipedia

  doAssert editDistance("привет", "превет") == 1
  doAssert editDistance("Åge", "Age") == 1
  # editDistance, one string is longer in bytes, but shorter in rune length
  # first string: 4 bytes, second: 6 bytes, but only 3 runes
  doAssert editDistance("aaaa", "×××") == 4

  block veryLongStringEditDistanceTest:
    const cap = 256
    var
      s1 = newStringOfCap(cap)
      s2 = newStringOfCap(cap)
    while len(s1) < cap:
      s1.add 'a'
    while len(s2) < cap:
      s2.add 'b'
    doAssert editDistance(s1, s2) == cap

  block combiningCodePointsEditDistanceTest:
    const s = "A\xCC\x8Age"
    doAssert editDistance(s, "Age") == 1

  doAssert editDistanceAscii("", "") == 0
  doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia
  doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia
indDotExpr: if e[1].kind == nkDotExpr: dotExpr = e[1] elif e[1].kind == nkCheckedFieldExpr: dotExpr = e[1][0] else: internalError(p.config, e.info, "unknown ast") let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) let member = if t.kind == tyTuple: "Field" & rope(dotExpr[1].sym.position) else: rope(dotExpr[1].sym.name.s) putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [getTypeDesc(p.module, t), member]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) of mXLenStr: if not p.module.compileToCpp: unaryExpr(p, e, d, "($1->Sup.len)") else: unaryExpr(p, e, d, "$1->len") of mXLenSeq: # see 'taddhigh.nim' for why we need to use a temporary here: var a, tmp: TLoc initLocExpr(p, e[1], a) getIntTemp(p, tmp) if not p.module.compileToCpp: lineCg(p, cpsStmts, "$1 = $2->Sup.len;$n", [tmp.r, rdLoc(a)]) else: lineCg(p, cpsStmts, "$1 = $2->len;$n", [tmp.r, rdLoc(a)]) putIntoDest(p, d, e, tmp.r) of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) of mCopyStr, mCopyStrLast: genCall(p, e, d) of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat: var opr = e.sons[0].sym # Why would anyone want to set nodecl to one of these hardcoded magics? # - not sure, and it wouldn't work if the symbol behind the magic isn't # somehow forward-declared from some other usage, but it is *possible* if lfNoDecl notin opr.loc.flags: let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r) # HACK: # Explicitly add this proc as declared here so the cgsym call doesn't # add a forward declaration - without this we could end up with the same # 2 forward declarations. That happens because the magic symbol and the original # one that shall be used have different ids (even though a call to one is # actually a call to the other) so checking into m.declaredProtos with the 2 different ids doesn't work. # Why would 2 identical forward declarations be a problem? # - in the case of hot code-reloading we generate function pointers instead # of forward declarations and in C++ it is an error to redefine a global let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id) # Make the function behind the magic get actually generated - this will # not lead to a forward declaration! The genCall will lead to one. discard cgsym(p.module, $opr.loc.r) # make sure we have pointer-initialising code for hot code reloading if not wasDeclared and p.hcrOn: addf(p.module.s[cfsDynLibInit], "\t$1 = ($2) hcrGetProc($3, \"$1\");$n", [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)]) genCall(p, e, d) of mDefault: genDefault(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s)) of mSpawn: when defined(leanCompiler): quit "compiler built without support for the 'spawn' statement" else: let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: when defined(leanCompiler): quit "compiler built without support for the 'parallel' statement" else: let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) expr(p, n, d) of mDeepCopy: var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) initLocExpr(p, e.sons[2], b) genDeepCopy(p, a, b) of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) of mMove: genMove(p, e, d) of mDestroy: genDestroy(p, e) of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & " 'toOpenArray' is only valid within a call expression") else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind internalError(p.config, e.info, "genMagicExpr: " & $op) proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } # we have to emit an expression of the form: # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var a, b, idx: TLoc if nfAllConst in e.flags: putIntoDest(p, d, e, genSetNode(p, e)) else: if d.k == locNone: getTemp(p, e.typ, d) if getSize(p.config, e.typ) > 8: # big set: linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n", [rdLoc(d), getTypeDesc(p.module, e.typ)]) for it in e.sons: if it.kind == nkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter initLocExpr(p, it.sons[0], a) initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) else: # small set var ts = "NU" & $(getSize(p.config, e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for it in e.sons: if it.kind == nkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter initLocExpr(p, it.sons[0], a) initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ), rope(ts)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n", [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc if not handleConstExpr(p, n, d): let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized if d.k == locNone: getTemp(p, t, d) for i in 0 ..< sonsLen(n): var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] initLoc(rec, locExpr, it, d.storage) rec.r = "$1.Field$2" % [rdLoc(d), rope(i)] rec.flags.incl(lfEnforceDeref) expr(p, it, rec) proc isConstClosure(n: PNode): bool {.inline.} = result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and n.sons[1].kind == nkNilLit proc genClosure(p: BProc, n: PNode, d: var TLoc) = assert n.kind in {nkPar, nkTupleConstr, nkClosure} if isConstClosure(n): inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[1], b) if n.sons[0].skipConv.kind == nkClosure: internalError(p.config, n.info, "closure to closure created") # tasyncawait.nim breaks with this optimization: when false: if d.k != locNone: linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n", [d.rdLoc, a.rdLoc, b.rdLoc]) else: getTemp(p, n.typ, tmp) linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp.rdLoc, a.rdLoc, b.rdLoc]) putLocIntoDest(p, d, tmp) proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = var arr: TLoc if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) for i in 0 ..< sonsLen(n): initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage) arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)] expr(p, n.sons[i], arr) proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) = requestConstImpl(p, sym) assert((sym.loc.r != nil) and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) template genStmtListExprImpl(exprOrStmt) {.dirty.} = #let hasNimFrame = magicsys.getCompilerProc("nimFrame") != nil let hasNimFrame = p.prc != nil and sfSystemModule notin p.module.module.flags and optStackTrace in p.prc.options var frameName: Rope = nil for i in 0 .. n.len - 2: let it = n[i] if it.kind == nkComesFrom: if hasNimFrame and frameName == nil: inc p.labels frameName = "FR" & rope(p.labels) & "_" let theMacro = it[0].sym add p.s(cpsStmts), initFrameNoDebug(p, frameName, makeCString theMacro.name.s, quotedFilename(p.config, theMacro.info), it.info.line.int) else: genStmts(p, it) if n.len > 0: exprOrStmt if frameName != nil: add p.s(cpsStmts), deinitFrameNoDebug(p, frameName) proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = genStmtListExprImpl: expr(p, n[n.len - 1], d) proc genStmtList(p: BProc, n: PNode) = genStmtListExprImpl: genStmts(p, n[n.len - 1]) proc upConv(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) let dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): var r = rdLoc(a) var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: if t.kind notin {tyVar, tyLent}: nilCheck = r if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = "(*$1)" % [r] t = skipTypes(t.lastSon, abstractInst) discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ".Sup") t = skipTypes(t.sons[0], skipPtrs) let checkFor = if optNimV2 in p.config.globalOptions: genTypeInfo2Name(p.module, dest) else: genTypeInfo(p.module, dest, n.info) if nilCheck != nil: linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", [nilCheck, r, checkFor]) else: linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", [r, checkFor]) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: putIntoDest(p, d, n, "(*($1*) ($2))" % [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) var arg = n.sons[0] while arg.kind == nkObjDownConv: arg = arg.sons[0] var src = skipTypes(arg.typ, abstractPtrs) discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent} if isRef: add(r, "->Sup") else: add(r, ".Sup") for i in 2 .. abs(inheritanceDiff(dest, src)): add(r, ".Sup") if isRef: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken # (see bug #837). However sometimes using a temporary is not correct: # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # this by ensuring the destination is also a pointer: if d.k == locNone and skipTypes(n.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}: getTemp(p, n.typ, d) linefmt(p, cpsStmts, "$1 = &$2;$n", [rdLoc(d), r]) else: r = "&" & r putIntoDest(p, d, n, r, a.storage) else: putIntoDest(p, d, n, r, a.storage) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) let tmp = p.module.tmpBase & rope(id) if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) else: putDataIntoDest(p, d, n, tmp) # This fixes bug #4551, but we really need better dataflow # analysis to make this 100% safe. if t.kind notin {tySequence, tyString}: d.storage = OnStatic proc expr(p: BProc, n: PNode, d: var TLoc) = p.currLineInfo = n.info case n.kind of nkSym: var sym = n.sym case sym.kind of skMethod: if {sfDispatcher, sfForward} * sym.flags != {}: # we cannot produce code for the dispatcher yet: fillProcLoc(p.module, n) genProcPrototype(p.module, sym) else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterator, skFunc: #if sym.kind == skIterator: # echo renderTree(sym.getBody, {renderIds}) if sfCompileTime in sym.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: # we never reach this case - as of the time of this comment, # skEnumField is folded to an int in semfold.nim, but this code # remains for robustness putIntoDest(p, d, n, rope(sym.position)) of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: genVarPrototype(p.module, n) if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(p.config): putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) else: putLocIntoDest(p, d, sym.loc) else: putLocIntoDest(p, d, sym.loc) of skTemp: if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: if sym.loc.r == nil or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) internalError(p.config, n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: if not isEmptyType(n.typ): putIntoDest(p, d, n, genLiteral(p, n)) of nkStrLit..nkTripleStrLit: putDataIntoDest(p, d, n, genLiteral(p, n)) of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit: putIntoDest(p, d, n, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: genLineDir(p, n) # may be redundant, it is generated in fixupCall as well let op = n.sons[0] if n.typ.isNil: # discard the value: var a: TLoc if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, a, op.sym.magic) else: genCall(p, n, a) else: # load it into 'd': if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, d, op.sym.magic) else: genCall(p, n, d) of nkCurly: if isDeepConstExpr(n) and n.len != 0: putIntoDest(p, d, n, genSetNode(p, n)) else: genSetConstr(p, n, d) of nkBracket: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) elif skipTypes(n.typ, abstractVarRange).kind == tySequence: genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) of nkPar, nkTupleConstr: if n.typ != nil and n.typ.kind == tyProc and n.len == 2: genClosure(p, n, d) elif isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: genTupleConstr(p, n, d) of nkObjConstr: genObjConstr(p, n, d) of nkCast: genCast(p, n, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) of nkHiddenAddr, nkAddr: genAddr(p, n, d) of nkBracketExpr: genBracketExpr(p, n, d) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) of nkDotExpr: genRecordField(p, n, d) of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) of nkBlockExpr, nkBlockStmt: genBlock(p, n, d) of nkStmtListExpr: genStmtListExpr(p, n, d) of nkStmtList: genStmtList(p, n) of nkIfExpr, nkIfStmt: genIf(p, n, d) of nkWhen: # This should be a "when nimvm" node. expr(p, n.sons[1].sons[0], d) of nkObjDownConv: downConv(p, n, d) of nkObjUpConv: upConv(p, n, d) of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") of nkChckRange64: genRangeChck(p, n, d, "chckRange64") of nkChckRange: genRangeChck(p, n, d, "chckRange") of nkStringToCString: convStrToCStr(p, n, d) of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: var sym = n.sons[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard # consts generated lazily on use of nkForStmt: internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: if nfPreventCg notin n.flags: genAsgn(p, n, fastAsgn=false) of nkFastAsgn: if nfPreventCg notin n.flags: # transf is overly aggressive with 'nkFastAsgn', so we work around here. # See tests/run/tcnstseq3 for an example that would fail otherwise. genAsgn(p, n, fastAsgn=p.prc != nil) of nkDiscardStmt: let ex = n[0] if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc initLocExprSingleUse(p, ex, a) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt, nkHiddenTryStmt: if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: genTryCpp(p, n, d) else: genTry(p, n, d) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection: # we have to emit the type information for object types here to support # separate compilation: genTypeSection(p.module, n) of nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: if n.sons[genericParamsPos].kind == nkEmpty: var prc = n.sons[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): # Generate proc even if empty body, bugfix #11651. genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) of nkState: genState(p, n) of nkGotoState: # simply never set it back to 0 here from here on... inc p.splitDecls genGotoState(p, n) of nkBreakState: genBreakState(p, n, d) else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode): Rope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) else: result = genConstExpr(p, n) proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) case t.kind of tyBool: result = rope"NIM_FALSE" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyFloat..tyFloat128: result = rope"0.0" of tyCString, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: result = rope"NIM_NIL" of tyString, tySequence: if p.config.selectedGC == gcDestructors: result = rope"{0, NIM_NIL}" else: result = rope"NIM_NIL" of tyProc: if t.callConv != ccClosure: result = rope"NIM_NIL" else: result = rope"{NIM_NIL, NIM_NIL}" of tyObject: if not isObjLackingTypeField(t) and not p.module.compileToCpp: result = "{{$1}}" % [genTypeInfo(p.module, t, info)] else: result = rope"{}" of tyTuple: result = rope"{" for i in 0 ..< typ.len: if i > 0: result.add ", " result.add getDefaultValue(p, typ.sons[i], info) result.add "}" of tyArray: result = rope"{}" of tySet: if mapType(p.config, t) == ctArray: result = rope"{}" else: result = rope"0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) = case obj.kind of nkRecList: for it in obj.sons: getNullValueAux(p, t, it, cons, result, count) of nkRecCase: getNullValueAux(p, t, obj.sons[0], cons, result, count) for i in 1 ..< sonsLen(obj): getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count) of nkSym: if count > 0: result.add ", " inc count let field = obj.sym for i in 1.. 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) addf(result, "}$n", []) proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: data.add(", {") for i in 0 ..< n.len: if i > 0: data.addf(",$n", []) data.add genConstExpr(p, n.sons[i]) data.add("}") data.add("}") result = getTempName(p.module) let base = t.skipTypes(abstractInst).sons[0] appcg(p.module, cfsData, "NIM_CONST struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ getTypeDesc(p.module, base), n.len, result, data]) result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] proc genConstSeqV2(p: BProc, n: PNode, t: PType): Rope = var data = rope"{" for i in 0 ..< n.len: if i > 0: data.addf(",$n", []) data.add genConstExpr(p, n.sons[i]) data.add("}") let payload = getTempName(p.module) let base = t.skipTypes(abstractInst).sons[0] appcg(p.module, cfsData, "static const struct {$n" & " NI cap; void* allocator; $1 data[$2];$n" & "} $3 = {$2, NIM_NIL, $4};$n", [ getTypeDesc(p.module, base), len(n), payload, data]) result = "{$1, ($2*)&$3}" % [rope(len(n)), getSeqPayloadType(p.module, t), payload] proc genConstExpr(p: BProc, n: PNode): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: result = genConstExpr(p, n.sons[1]) of nkCurly: var cs: TBitSet toBitSet(p.config, n, cs) result = genRawSetData(cs, int(getSize(p.config, n.typ))) of nkBracket, nkPar, nkTupleConstr, nkClosure: var t = skipTypes(n.typ, abstractInstOwned) if t.kind == tySequence: if p.config.selectedGC == gcDestructors: result = genConstSeqV2(p, n, n.typ) else: result = genConstSeq(p, n, n.typ) elif t.kind == tyProc and t.callConv == ccClosure and n.len > 1 and n.sons[1].kind == nkNilLit: # Conversion: nimcall -> closure. # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be # expanded to {NIM_NIL,NIM_NIL} # in VM closures are initialized with nkPar(nkNilLit, nkNilLit) # leading to duplicate code like this: # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" if n[0].kind == nkNilLit: result = ~"{NIM_NIL,NIM_NIL}" else: var d: TLoc initLocExpr(p, n[0], d) result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, t, clHalfWithEnv), rdLoc(d)] else: result = genConstSimpleList(p, n) of nkObjConstr: result = genConstObjConstr(p, n) of nkStrLit..nkTripleStrLit: if p.config.selectedGC == gcDestructors: result = genStringLiteralV2Const(p.module, n) else: var d: TLoc initLocExpr(p, n, d) result = rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) result = rdLoc(d)