GC: get rid of pathological behaviour for stack marking

This commit is contained in:
Araq 2015-03-08 13:05:44 +01:00
commit c40aac8e20
5 changed files with 326 additions and 323 deletions

View file

@ -10,15 +10,15 @@
# This module implements the passes functionality. A pass must implement the # This module implements the passes functionality. A pass must implement the
# `TPass` interface. # `TPass` interface.
import import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen nimsets, syntaxes, times, rodread, idgen
type type
TPassContext* = object of RootObj # the pass's context TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached" fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext PPassContext* = ref TPassContext
TPassOpen* = proc (module: PSym): PPassContext {.nimcall.} TPassOpen* = proc (module: PSym): PPassContext {.nimcall.}
@ -33,8 +33,8 @@ type
TPassData* = tuple[input: PNode, closeOutput: PNode] TPassData* = tuple[input: PNode, closeOutput: PNode]
TPasses* = openArray[TPass] TPasses* = openArray[TPass]
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures. # nodes. These are passed to the other close procedures.
# This mechanism used to be used for the instantiation of generics. # This mechanism used to be used for the instantiation of generics.
proc makePass*(open: TPassOpen = nil, proc makePass*(open: TPassOpen = nil,
@ -53,46 +53,46 @@ proc makePass*(open: TPassOpen = nil,
proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader) proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
# the semantic checker needs these: # the semantic checker needs these:
var var
gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.} gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.} gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.}
# implementation # implementation
proc skipCodegen*(n: PNode): bool {.inline.} = proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do # can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global # something with `n`. Currently, this ignores `n` and uses the global
# error count instead. # error count instead.
result = msgs.gErrorCounter > 0 result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool = proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc # The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when # needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important # appropriate to free the procedure body's memory. This is important
# to keep memory usage down. # to keep memory usage down.
if (s.kind in {skMethod, skProc}) and if (s.kind in {skMethod, skProc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and (s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty): (s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false result = false
# XXX this doesn't really make sense with excessive CTFE # XXX this doesn't really make sense with excessive CTFE
else: else:
result = true result = true
const const
maxPasses = 10 maxPasses = 10
type type
TPassContextArray = array[0..maxPasses - 1, PPassContext] TPassContextArray = array[0..maxPasses - 1, PPassContext]
var var
gPasses: array[0..maxPasses - 1, TPass] gPasses: array[0..maxPasses - 1, TPass]
gPassesLen*: int gPassesLen*: int
proc clearPasses* = proc clearPasses* =
gPassesLen = 0 gPassesLen = 0
proc registerPass*(p: TPass) = proc registerPass*(p: TPass) =
gPasses[gPassesLen] = p gPasses[gPassesLen] = p
inc(gPassesLen) inc(gPassesLen)
@ -109,48 +109,48 @@ proc carryPasses*(nodes: PNode, module: PSym, passes: TPasses) =
passdata = carryPass(pass, module, passdata) passdata = carryPass(pass, module, passdata)
proc openPasses(a: var TPassContextArray, module: PSym) = proc openPasses(a: var TPassContextArray, module: PSym) =
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open): if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module) a[i] = gPasses[i].open(module)
else: a[i] = nil else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) = proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) =
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, rd) a[i] = gPasses[i].openCached(module, rd)
if a[i] != nil: if a[i] != nil:
a[i].fromCache = true a[i].fromCache = true
else: else:
a[i] = nil a[i] = nil
proc closePasses(a: var TPassContextArray) = proc closePasses(a: var TPassContextArray) =
var m: PNode = nil var m: PNode = nil
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m) if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here a[i] = nil # free the memory here
proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
# this implements the code transformation pipeline # this implements the code transformation pipeline
var m = n var m = n
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process): if not isNil(gPasses[i].process):
m = gPasses[i].process(a[i], m) m = gPasses[i].process(a[i], m)
if isNil(m): return false if isNil(m): return false
result = true result = true
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
# this implements the code transformation pipeline # this implements the code transformation pipeline
var m = n var m = n
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m) if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
proc closePassesCached(a: var TPassContextArray) = proc closePassesCached(a: var TPassContextArray) =
var m: PNode = nil var m: PNode = nil
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
m = gPasses[i].close(a[i], m) m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here a[i] = nil # free the memory here
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray) = a: var TPassContextArray) =
for module in items(implicits): for module in items(implicits):
@ -159,45 +159,45 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
str.info = gCmdLineInfo str.info = gCmdLineInfo
importStmt.addSon str importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break if not processTopLevelStmt(importStmt, a): break
proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
var var
p: TParsers p: TParsers
a: TPassContextArray a: TPassContextArray
s: PLLStream s: PLLStream
fileIdx = module.fileIdx fileIdx = module.fileIdx
if rd == nil: if rd == nil:
openPasses(a, module) openPasses(a, module)
if stream == nil: if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-": if module.name.s == "-":
module.name.s = "stdinfile" module.name.s = "stdinfile"
s = llStreamOpen(stdin) s = llStreamOpen(stdin)
else: else:
s = llStreamOpen(filename, fmRead) s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return
else: else:
s = stream s = stream
while true: while true:
openParsers(p, fileIdx, s) openParsers(p, fileIdx, s)
if sfSystemModule notin module.flags: if sfSystemModule notin module.flags:
# XXX what about caching? no processing then? what if I change the # XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that # modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only # in ROD files. I think we should enable this feature only
# for the interactive mode. # for the interactive mode.
processImplicits implicitImports, nkImportStmt, a processImplicits implicitImports, nkImportStmt, a
processImplicits implicitIncludes, nkIncludeStmt, a processImplicits implicitIncludes, nkIncludeStmt, a
while true: while true:
var n = parseTopLevelStmt(p) var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break if n.kind == nkEmpty: break
if not processTopLevelStmt(n, a): break if not processTopLevelStmt(n, a): break
closeParsers(p) closeParsers(p)
if s.kind != llsStdIn: break if s.kind != llsStdIn: break
closePasses(a) closePasses(a)
# id synchronization point for more consistent code generation: # id synchronization point for more consistent code generation:
idSynchronizationPoint(1000) idSynchronizationPoint(1000)

View file

@ -17,27 +17,27 @@
# * introduces method dispatchers # * introduces method dispatchers
# * performs lambda lifting for closure support # * performs lambda lifting for closure support
import import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings lambdalifting, sempass2, lowerings
# implementation # implementation
type type
PTransNode* = distinct PNode PTransNode* = distinct PNode
PTransCon = ref TTransCon PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable TTransCon{.final.} = object # part of TContext; stackable
mapping: TIdNodeTable # mapping from symbols to nodes mapping: TIdNodeTable # mapping from symbols to nodes
owner: PSym # current owner owner: PSym # current owner
forStmt: PNode # current for stmt forStmt: PNode # current for stmt
forLoopBody: PTransNode # transformed for loop body forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements, yieldStmts: int # we count the number of yield statements,
# because we need to introduce new variables # because we need to introduce new variables
# if we encounter the 2nd yield statement # if we encounter the 2nd yield statement
next: PTransCon # for stacking next: PTransCon # for stacking
TTransfContext = object of passes.TPassContext TTransfContext = object of passes.TPassContext
module: PSym module: PSym
transCon: PTransCon # top of a TransCon stack transCon: PTransCon # top of a TransCon stack
@ -46,52 +46,52 @@ type
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
PTransf = ref TTransfContext PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} = proc newTransNode(a: PNode): PTransNode {.inline.} =
result = PTransNode(shallowCopy(a)) result = PTransNode(shallowCopy(a))
proc newTransNode(kind: TNodeKind, info: TLineInfo, proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} = sons: int): PTransNode {.inline.} =
var x = newNodeI(kind, info) var x = newNodeI(kind, info)
newSeq(x.sons, sons) newSeq(x.sons, sons)
result = x.PTransNode result = x.PTransNode
proc newTransNode(kind: TNodeKind, n: PNode, proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} = sons: int): PTransNode {.inline.} =
var x = newNodeIT(kind, n.info, n.typ) var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons) newSeq(x.sons, sons)
x.typ = n.typ x.typ = n.typ
result = x.PTransNode result = x.PTransNode
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} = proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a) var n = PNode(a)
n.sons[i] = PNode(x) n.sons[i] = PNode(x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} = proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a) var n = PNode(a)
result = n.sons[i].PTransNode result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b)) proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode) proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
proc newTransCon(owner: PSym): PTransCon = proc newTransCon(owner: PSym): PTransCon =
assert owner != nil assert owner != nil
new(result) new(result)
initIdNodeTable(result.mapping) initIdNodeTable(result.mapping)
result.owner = owner result.owner = owner
proc pushTransCon(c: PTransf, t: PTransCon) = proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon t.next = c.transCon
c.transCon = t c.transCon = t
proc popTransCon(c: PTransf) = proc popTransCon(c: PTransf) =
if (c.transCon == nil): internalError("popTransCon") if (c.transCon == nil): internalError("popTransCon")
c.transCon = c.transCon.next c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym = proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner if c.transCon != nil: result = c.transCon.owner
else: result = c.module else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym = proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst}) result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric) incl(result.flags, sfFromGeneric)
@ -100,10 +100,10 @@ proc transform(c: PTransf, n: PNode): PTransNode
proc transformSons(c: PTransf, n: PNode): PTransNode = proc transformSons(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n) result = newTransNode(n)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
result[i] = transform(c, n.sons[i]) result[i] = transform(c, n.sons[i])
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result = newTransNode(nkFastAsgn, PNode(ri).info, 2) result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le) result[0] = PTransNode(le)
result[1] = ri result[1] = ri
@ -113,30 +113,30 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
# return liftIterSym(n) # return liftIterSym(n)
var b: PNode var b: PNode
var tc = c.transCon var tc = c.transCon
if sfBorrow in n.sym.flags: if sfBorrow in n.sym.flags:
# simply exchange the symbol: # simply exchange the symbol:
b = n.sym.getBody b = n.sym.getBody
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol") if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym) b = newSymNode(b.sym)
b.info = n.info b.info = n.info
else: else:
b = n b = n
while tc != nil: while tc != nil:
result = idNodeTableGet(tc.mapping, b.sym) result = idNodeTableGet(tc.mapping, b.sym)
if result != nil: return if result != nil: return
tc = tc.next tc = tc.next
result = b result = b
proc transformSym(c: PTransf, n: PNode): PTransNode = proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n)) result = PTransNode(transformSymAux(c, n))
proc transformVarSection(c: PTransf, v: PNode): PTransNode = proc transformVarSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v) result = newTransNode(v)
for i in countup(0, sonsLen(v)-1): for i in countup(0, sonsLen(v)-1):
var it = v.sons[i] var it = v.sons[i]
if it.kind == nkCommentStmt: if it.kind == nkCommentStmt:
result[i] = PTransNode(it) result[i] = PTransNode(it)
elif it.kind == nkIdentDefs: elif it.kind == nkIdentDefs:
if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection") if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection")
internalAssert(it.len == 3) internalAssert(it.len == 3)
var newVar = copySym(it.sons[0].sym) var newVar = copySym(it.sons[0].sym)
@ -153,12 +153,12 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
defs[1] = it.sons[1].PTransNode defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2]) defs[2] = transform(c, it.sons[2])
result[i] = defs result[i] = defs
else: else:
if it.kind != nkVarTuple: if it.kind != nkVarTuple:
internalError(it.info, "transformVarSection: not nkVarTuple") internalError(it.info, "transformVarSection: not nkVarTuple")
var L = sonsLen(it) var L = sonsLen(it)
var defs = newTransNode(it.kind, it.info, L) var defs = newTransNode(it.kind, it.info, L)
for j in countup(0, L-3): for j in countup(0, L-3):
var newVar = copySym(it.sons[j].sym) var newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric) incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c) newVar.owner = getCurrOwner(c)
@ -188,12 +188,12 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
else: else:
result[i] = PTransNode(it) result[i] = PTransNode(it)
proc hasContinue(n: PNode): bool = proc hasContinue(n: PNode): bool =
case n.kind case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
of nkContinueStmt: result = true of nkContinueStmt: result = true
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym = proc newLabel(c: PTransf, n: PNode): PSym =
@ -224,10 +224,10 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode =
discard c.breakSyms.pop discard c.breakSyms.pop
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
proc transformLoopBody(c: PTransf, n: PNode): PTransNode = proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs # What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same! # an explicit label too, but not the same!
# We fix this here by making every 'break' belong to its enclosing loop # We fix this here by making every 'break' belong to its enclosing loop
# and changing all breaks that belong to a 'block' by annotating it with # and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already). # a label (if it hasn't one already).
@ -239,7 +239,7 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n) result[1] = transform(c, n)
discard c.contSyms.pop() discard c.contSyms.pop()
else: else:
result = transform(c, n) result = transform(c, n)
proc transformWhile(c: PTransf; n: PNode): PTransNode = proc transformWhile(c: PTransf; n: PNode): PTransNode =
@ -273,27 +273,27 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
# XXX: BUG: what if `n` is an expression with side-effects? # XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(c.transCon.forStmt) - 3): for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i)))) transform(c, newTupleAccess(n, i))))
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
result = transformVarSection(c, n) result = transformVarSection(c, n)
else: else:
result = newTransNode(n) result = newTransNode(n)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
result[i] = introduceNewLocalVars(c, n.sons[i]) result[i] = introduceNewLocalVars(c, n.sons[i])
proc transformYield(c: PTransf, n: PNode): PTransNode = proc transformYield(c: PTransf, n: PNode): PTransNode =
result = newTransNode(nkStmtList, n.info, 0) result = newTransNode(nkStmtList, n.info, 0)
var e = n.sons[0] var e = n.sons[0]
# c.transCon.forStmt.len == 3 means that there is one for loop variable # c.transCon.forStmt.len == 3 means that there is one for loop variable
@ -301,21 +301,21 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
c.transCon.forStmt.len != 3: c.transCon.forStmt.len != 3:
e = skipConv(e) e = skipConv(e)
if e.kind == nkPar: if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1): for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i], add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, e.sons[i]))) transform(c, e.sons[i])))
else: else:
unpackTuple(c, e, result) unpackTuple(c, e, result)
else: else:
var x = transform(c, e) var x = transform(c, e)
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x)) add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
inc(c.transCon.yieldStmts) inc(c.transCon.yieldStmts)
if c.transCon.yieldStmts <= 1: if c.transCon.yieldStmts <= 1:
# common case # common case
add(result, c.transCon.forLoopBody) add(result, c.transCon.forLoopBody)
else: else:
# we need to introduce new local variables: # we need to introduce new local variables:
add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode)) add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
@ -340,25 +340,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
if n.sons[0].kind == a or n.sons[0].kind == b: if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x # addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0]) result = PTransNode(n.sons[0].sons[0])
proc transformConv(c: PTransf, n: PNode): PTransNode = proc transformConv(c: PTransf, n: PNode): PTransNode =
# numeric types need range checks: # numeric types need range checks:
var dest = skipTypes(n.typ, abstractVarRange) var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange) var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32: of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
# we don't include uint and uint64 here as these are no ordinal types ;-) # we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source): if not isOrdinalType(source):
# float -> int conversions. ugh. # float -> int conversions. ugh.
result = transformSons(c, n) result = transformSons(c, n)
elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
lastOrd(n.sons[1].typ) <= lastOrd(n.typ): lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
# BUGFIX: simply leave n as it is; we need a nkConv node, # BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check: # but no range check:
result = transformSons(c, n) result = transformSons(c, n)
else: else:
# generate a range check: # generate a range check:
if dest.kind == tyInt64 or source.kind == tyInt64: if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3) result = newTransNode(nkChckRange64, n, 3)
else: else:
result = newTransNode(nkChckRange, n, 3) result = newTransNode(nkChckRange, n, 3)
@ -368,7 +368,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
# XXX int64 -> float conversion? # XXX int64 -> float conversion?
if skipTypes(n.typ, abstractVar).kind == tyRange: if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3) result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar) dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
@ -378,81 +378,81 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
of tyCString: of tyCString:
if source.kind == tyString: if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1) result = newTransNode(nkStringToCString, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyString: of tyString:
if source.kind == tyCString: if source.kind == tyCString:
result = newTransNode(nkCStringToString, n, 1) result = newTransNode(nkCStringToString, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyRef, tyPtr: of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs) dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs) source = skipTypes(source, abstractPtrs)
if source.kind == tyObject: if source.kind == tyObject:
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyObject: of tyObject:
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal: of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc. # happens sometimes for generated assignments, etc.
else: else:
result = transformSons(c, n) result = transformSons(c, n)
type type
TPutArgInto = enum TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn paDirectMapping, paFastAsgn, paVarAsgn
proc putArgInto(arg: PNode, formal: PType): TPutArgInto = proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
# This analyses how to treat the mapping "formal <-> arg" in an # This analyses how to treat the mapping "formal <-> arg" in an
# inline context. # inline context.
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}: if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
return paDirectMapping # XXX really correct? return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects? # what if ``arg`` has side-effects?
case arg.kind case arg.kind
of nkEmpty..nkNilLit: of nkEmpty..nkNilLit:
result = paDirectMapping result = paDirectMapping
of nkPar, nkCurly, nkBracket: of nkPar, nkCurly, nkBracket:
result = paFastAsgn result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1): for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping result = paDirectMapping
else: else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) = proc findWrongOwners(c: PTransf, n: PNode) =
if n.kind == nkVarSection: if n.kind == nkVarSection:
let x = n.sons[0].sons[0] let x = n.sons[0].sons[0]
if x.kind == nkSym and x.sym.owner != getCurrOwner(c): if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
internalError(x.info, "bah " & x.sym.name.s & " " & internalError(x.info, "bah " & x.sym.name.s & " " &
x.sym.owner.name.s & " " & getCurrOwner(c).name.s) x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
else: else:
for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i]) for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i])
proc transformFor(c: PTransf, n: PNode): PTransNode = proc transformFor(c: PTransf, n: PNode): PTransNode =
# generate access statements for the parameters (unless they are constant) # generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters # put mapping from formal parameters to actual parameters
if n.kind != nkForStmt: internalError(n.info, "transformFor") if n.kind != nkForStmt: internalError(n.info, "transformFor")
@ -466,26 +466,26 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
if call.typ.kind != tyIter and if call.typ.kind != tyIter and
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].sym.kind != skIterator): call.sons[0].sym.kind != skIterator):
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
result[1] = lambdalifting.liftForLoop(n).PTransNode result[1] = lambdalifting.liftForLoop(n).PTransNode
discard c.breakSyms.pop discard c.breakSyms.pop
return result return result
#echo "transforming: ", renderTree(n) #echo "transforming: ", renderTree(n)
var stmtList = newTransNode(nkStmtList, n.info, 0) var stmtList = newTransNode(nkStmtList, n.info, 0)
var loopBody = transformLoopBody(c, n.sons[length-1]) var loopBody = transformLoopBody(c, n.sons[length-1])
result[1] = stmtList result[1] = stmtList
discard c.breakSyms.pop discard c.breakSyms.pop
var v = newNodeI(nkVarSection, n.info) var v = newNodeI(nkVarSection, n.info)
for i in countup(0, length - 3): for i in countup(0, length - 3):
addVar(v, copyTree(n.sons[i])) # declare new vars addVar(v, copyTree(n.sons[i])) # declare new vars
add(stmtList, v.PTransNode) add(stmtList, v.PTransNode)
# Bugfix: inlined locals belong to the invoking routine, not to the invoked # Bugfix: inlined locals belong to the invoking routine, not to the invoked
# iterator! # iterator!
let iter = call.sons[0].sym let iter = call.sons[0].sym
@ -496,9 +496,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
if iter.kind != skIterator: return result if iter.kind != skIterator: return result
# generate access statements for the parameters (unless they are constant) # generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC) pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1): for i in countup(1, sonsLen(call) - 1):
var arg = transform(c, call.sons[i]).PNode var arg = transform(c, call.sons[i]).PNode
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
if arg.typ.kind == tyIter: continue if arg.typ.kind == tyIter: continue
case putArgInto(arg, formal.typ) case putArgInto(arg, formal.typ)
of paDirectMapping: of paDirectMapping:
@ -527,20 +527,20 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
popInfoContext() popInfoContext()
popTransCon(c) popTransCon(c)
# echo "transformed: ", stmtList.PNode.renderTree # echo "transformed: ", stmtList.PNode.renderTree
proc getMagicOp(call: PNode): TMagic = proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and if call.sons[0].kind == nkSym and
call.sons[0].sym.kind in {skProc, skMethod, skConverter}: call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
result = call.sons[0].sym.magic result = call.sons[0].sym.magic
else: else:
result = mNone result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode = proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt # removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator # adds ``else: nil`` if needed for the code generator
result = newTransNode(nkCaseStmt, n, 0) result = newTransNode(nkCaseStmt, n, 0)
var ifs = PTransNode(nil) var ifs = PTransNode(nil)
for i in 0 .. sonsLen(n)-1: for i in 0 .. sonsLen(n)-1:
var it = n.sons[i] var it = n.sons[i]
var e = transform(c, it) var e = transform(c, it)
case it.kind case it.kind
@ -564,8 +564,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
var elseBranch = newTransNode(nkElse, n.info, 1) var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0) elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
add(result, elseBranch) add(result, elseBranch)
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode = proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
# XXX this is really bad; transf should use a proper AST visitor # XXX this is really bad; transf should use a proper AST visitor
if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType: if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
result = n.PTransNode result = n.PTransNode
@ -573,45 +573,45 @@ proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n) result = newTransNode(n)
for i in 0 .. < n.len: for i in 0 .. < n.len:
result[i] = transform(c, skipConv(n.sons[i])) result[i] = transform(c, skipConv(n.sons[i]))
proc getMergeOp(n: PNode): PSym = proc getMergeOp(n: PNode): PSym =
case n.kind case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and
(sfMerge in n.sons[0].sym.flags): (sfMerge in n.sons[0].sym.flags):
result = n.sons[0].sym result = n.sons[0].sym
else: discard else: discard
proc flattenTreeAux(d, a: PNode, op: PSym) = proc flattenTreeAux(d, a: PNode, op: PSym) =
let op2 = getMergeOp(a) let op2 = getMergeOp(a)
if op2 != nil and if op2 != nil and
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic): (op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op) for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
else: else:
addSon(d, copyTree(a)) addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode = proc flattenTree(root: PNode): PNode =
let op = getMergeOp(root) let op = getMergeOp(root)
if op != nil: if op != nil:
result = copyNode(root) result = copyNode(root)
addSon(result, copyTree(root.sons[0])) addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op) flattenTreeAux(result, root, op)
else: else:
result = root result = root
proc transformCall(c: PTransf, n: PNode): PTransNode = proc transformCall(c: PTransf, n: PNode): PTransNode =
var n = flattenTree(n) var n = flattenTree(n)
let op = getMergeOp(n) let op = getMergeOp(n)
let magic = getMagic(n) let magic = getMagic(n)
if op != nil and op.magic != mNone and n.len >= 3: if op != nil and op.magic != mNone and n.len >= 3:
result = newTransNode(nkCall, n, 0) result = newTransNode(nkCall, n, 0)
add(result, transform(c, n.sons[0])) add(result, transform(c, n.sons[0]))
var j = 1 var j = 1
while j < sonsLen(n): while j < sonsLen(n):
var a = transform(c, n.sons[j]).PNode var a = transform(c, n.sons[j]).PNode
inc(j) inc(j)
if isConstExpr(a): if isConstExpr(a):
while (j < sonsLen(n)): while (j < sonsLen(n)):
let b = transform(c, n.sons[j]).PNode let b = transform(c, n.sons[j]).PNode
if not isConstExpr(b): break if not isConstExpr(b): break
@ -640,7 +640,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be # symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array: # inlined, unless it's the empty array:
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
cnst.len != 0 cnst.len != 0
proc commonOptimizations*(c: PSym, n: PNode): PNode = proc commonOptimizations*(c: PSym, n: PNode): PNode =
@ -673,11 +673,11 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
else: else:
result = n result = n
proc transform(c: PTransf, n: PNode): PTransNode = proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n) of nkBracketExpr: result = transformArrayAccess(c, n)
@ -702,7 +702,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
n.sons[bodyPos] = PNode(transform(c, s.getBody)) n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false) if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n) result = PTransNode(n)
of nkForStmt: of nkForStmt:
result = transformFor(c, n) result = transformFor(c, n)
of nkParForStmt: of nkParForStmt:
result = transformSons(c, n) result = transformSons(c, n)
@ -713,14 +713,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
add(result, PTransNode(newSymNode(labl))) add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n) of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: result = transformWhile(c, n) of nkWhileStmt: result = transformWhile(c, n)
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
result = transformCall(c, n) result = transformCall(c, n)
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref: of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr) result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n) result = transformConv(c, n)
of nkDiscardStmt: of nkDiscardStmt:
result = PTransNode(n) result = PTransNode(n)
@ -730,7 +730,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# ensure that e.g. discard "some comment" gets optimized away # ensure that e.g. discard "some comment" gets optimized away
# completely: # completely:
result = PTransNode(newNode(nkCommentStmt)) result = PTransNode(newNode(nkCommentStmt))
of nkCommentStmt, nkTemplateDef: of nkCommentStmt, nkTemplateDef:
return n.PTransNode return n.PTransNode
of nkConstSection: of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3`` # do not replace ``const c = 3`` with ``const 3 = 3``
@ -744,10 +744,10 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformVarSection(c, n) result = transformVarSection(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of nkYieldStmt: of nkYieldStmt:
if c.inlining > 0: if c.inlining > 0:
result = transformYield(c, n) result = transformYield(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr: of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n) result = transformBlock(c, n)
@ -764,7 +764,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst): if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization result = PTransNode(cnst) # do not miss an optimization
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous # this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node. # nodes into an empty node.
@ -774,7 +774,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
popTransCon(c) popTransCon(c)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PTransf = proc openTransf(module: PSym, filename: string): PTransf =
new(result) new(result)
result.contSyms = @[] result.contSyms = @[]
result.breakSyms = @[] result.breakSyms = @[]

View file

@ -89,7 +89,7 @@ type
SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32 SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32
## type class matching all ordinal types; however this includes enums with ## type class matching all ordinal types; however this includes enums with
## holes. ## holes.
SomeReal* = float|float32|float64 SomeReal* = float|float32|float64
## type class matching all floating point number types ## type class matching all floating point number types
@ -181,7 +181,7 @@ proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {.
## freeing the object. Note: The `finalizer` refers to the type `T`, not to ## freeing the object. Note: The `finalizer` refers to the type `T`, not to
## the object! This means that for each object of type `T` the finalizer ## the object! This means that for each object of type `T` the finalizer
## will be called! ## will be called!
proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
## resets an object `obj` to its initial (binary zero) value. This needs to ## resets an object `obj` to its initial (binary zero) value. This needs to
## be called before any possible `object branch transition`:idx:. ## be called before any possible `object branch transition`:idx:.
@ -348,7 +348,7 @@ type
## This field is filled automatically in the ## This field is filled automatically in the
## ``raise`` statement. ## ``raise`` statement.
msg* {.exportc: "message".}: string ## the exception's message. Not msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message ## providing an exception message
## is bad style. ## is bad style.
trace: string trace: string
@ -483,7 +483,7 @@ type
E_Base: Exception, ESystem: SystemError, EIO: IOError, E_Base: Exception, ESystem: SystemError, EIO: IOError,
EOS: OSError, EInvalidLibrary: LibraryError, EOS: OSError, EInvalidLibrary: LibraryError,
EResourceExhausted: ResourceExhaustedError, EResourceExhausted: ResourceExhaustedError,
EArithmetic: ArithmeticError, EDivByZero: DivByZeroError, EArithmetic: ArithmeticError, EDivByZero: DivByZeroError,
EOverflow: OverflowError, EAccessViolation: AccessViolationError, EOverflow: OverflowError, EAccessViolation: AccessViolationError,
EAssertionFailed: AssertionError, EInvalidValue: ValueError, EAssertionFailed: AssertionError, EInvalidValue: ValueError,
@ -494,7 +494,7 @@ type
EInvalidObjectAssignment: ObjectAssignmentError, EInvalidObjectAssignment: ObjectAssignmentError,
EInvalidObjectConversion: ObjectConversionError, EInvalidObjectConversion: ObjectConversionError,
EDeadThread: DeadThreadError, EDeadThread: DeadThreadError,
EFloatInexact: FloatInexactError, EFloatInexact: FloatInexactError,
EFloatUnderflow: FloatUnderflowError, EFloatUnderflow: FloatUnderflowError,
EFloatingPoint: FloatingPointError, EFloatingPoint: FloatingPointError,
EFloatInvalidOp: FloatInvalidOpError, EFloatInvalidOp: FloatInvalidOpError,
@ -511,11 +511,11 @@ proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.}
proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.}
## unary ``<`` that can be used for nice looking excluding ranges: ## unary ``<`` that can be used for nice looking excluding ranges:
## ##
## .. code-block:: nim ## .. code-block:: nim
## for i in 0 .. <10: echo i ## for i in 0 .. <10: echo i
## ##
## Semantically this is the same as ``pred``. ## Semantically this is the same as ``pred``.
proc succ*[T](x: Ordinal[T], y = 1): T {.magic: "Succ", noSideEffect.} proc succ*[T](x: Ordinal[T], y = 1): T {.magic: "Succ", noSideEffect.}
## returns the ``y``-th successor of the value ``x``. ``T`` has to be ## returns the ``y``-th successor of the value ``x``. ``T`` has to be
@ -536,7 +536,7 @@ proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.
## decrements the ordinal ``x`` by ``y``. If such a value does not ## decrements the ordinal ``x`` by ``y``. If such a value does not
## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a
## short notation for: ``x = pred(x, y)``. ## short notation for: ``x = pred(x, y)``.
proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
## creates a new sequence of type ``seq[T]`` with length ``len``. ## creates a new sequence of type ``seq[T]`` with length ``len``.
## This is equivalent to ``s = @[]; setlen(s, len)``, but more ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
@ -636,7 +636,7 @@ when not defined(JS):
proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.} proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.}
## treats `x` as unsigned and converts it to a byte by taking the last 8 bits ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits
## from `x`. ## from `x`.
proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.} proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.}
## treats `x` as unsigned and converts it to an ``int16`` by taking the last ## treats `x` as unsigned and converts it to an ``int16`` by taking the last
## 16 bits from `x`. ## 16 bits from `x`.
@ -800,7 +800,7 @@ proc `%%` *(x, y: int64): int64 {.magic: "ModU", noSideEffect.}
## The result is truncated to fit into the result. ## The result is truncated to fit into the result.
## This implements modulo arithmetic. ## This implements modulo arithmetic.
## No overflow errors are possible. ## No overflow errors are possible.
proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.} proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.}
proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.} proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.}
## treats `x` and `y` as unsigned and compares them. ## treats `x` and `y` as unsigned and compares them.
@ -889,7 +889,7 @@ template `notin` * (x, y: expr): expr {.immediate, dirty.} = not contains(y, x)
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
## Checks if T is of the same type as S ## Checks if T is of the same type as S
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc test[T](a: T): int = ## proc test[T](a: T): int =
## when (T is int): ## when (T is int):
@ -924,7 +924,7 @@ proc cmp*(x, y: string): int {.noSideEffect, procvar.}
proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {. proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
magic: "ArrToSeq", nosideeffect.} magic: "ArrToSeq", nosideeffect.}
## turns an array into a sequence. This most often useful for constructing ## turns an array into a sequence. This most often useful for constructing
## sequences with the array constructor: ``@[1, 2, 3]`` has the type ## sequences with the array constructor: ``@[1, 2, 3]`` has the type
## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``.
proc setLen*[T](s: var seq[T], newlen: int) {. proc setLen*[T](s: var seq[T], newlen: int) {.
@ -933,14 +933,14 @@ proc setLen*[T](s: var seq[T], newlen: int) {.
## ``T`` may be any sequence type. ## ``T`` may be any sequence type.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with
## a size, use ``newSeq`` instead. ## a size, use ``newSeq`` instead.
proc setLen*(s: var string, newlen: int) {. proc setLen*(s: var string, newlen: int) {.
magic: "SetLengthStr", noSideEffect.} magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a string with ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with
## a size, use ``newString`` instead. ## a size, use ``newString`` instead.
proc newString*(len: int): string {. proc newString*(len: int): string {.
magic: "NewString", importc: "mnewString", noSideEffect.} magic: "NewString", importc: "mnewString", noSideEffect.}
@ -953,7 +953,7 @@ proc newString*(len: int): string {.
proc newStringOfCap*(cap: int): string {. proc newStringOfCap*(cap: int): string {.
magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.} magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.}
## returns a new string of length ``0`` but with capacity `cap`.This ## returns a new string of length ``0`` but with capacity `cap`.This
## procedure exists only for optimization purposes; the same effect can ## procedure exists only for optimization purposes; the same effect can
## be achieved with the ``&`` operator or with ``add``. ## be achieved with the ``&`` operator or with ``add``.
proc `&` * (x: string, y: char): string {. proc `&` * (x: string, y: char): string {.
@ -982,7 +982,7 @@ proc `&` * (x: char, y: string): string {.
## assert('a' & "bc" == "abc") ## assert('a' & "bc" == "abc")
# implementation note: These must all have the same magic value "ConStrStr" so # implementation note: These must all have the same magic value "ConStrStr" so
# that the merge optimization works properly. # that the merge optimization works properly.
proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.}
## Appends `y` to `x` in place ## Appends `y` to `x` in place
@ -1039,15 +1039,15 @@ proc compileOption*(option: string): bool {.
## can be used to determine an on|off compile-time option. Example: ## can be used to determine an on|off compile-time option. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## when compileOption("floatchecks"): ## when compileOption("floatchecks"):
## echo "compiled with floating point NaN and Inf checks" ## echo "compiled with floating point NaN and Inf checks"
proc compileOption*(option, arg: string): bool {. proc compileOption*(option, arg: string): bool {.
magic: "CompileOptionArg", noSideEffect.} magic: "CompileOptionArg", noSideEffect.}
## can be used to determine an enum compile-time option. Example: ## can be used to determine an enum compile-time option. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## when compileOption("opt", "size") and compileOption("gc", "boehm"): ## when compileOption("opt", "size") and compileOption("gc", "boehm"):
## echo "compiled with optimization for size and uses Boehm's GC" ## echo "compiled with optimization for size and uses Boehm's GC"
const const
@ -1056,16 +1056,16 @@ const
taintMode = compileOption("taintmode") taintMode = compileOption("taintmode")
when taintMode: when taintMode:
type TaintedString* = distinct string ## a distinct string type that type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for ## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not ## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode`` ## turned on. Use the ``-d:taintMode``
## command line switch to turn the taint ## command line switch to turn the taint
## mode on. ## mode on.
proc len*(s: TaintedString): int {.borrow.} proc len*(s: TaintedString): int {.borrow.}
else: else:
type TaintedString* = string ## a distinct string type that type TaintedString* = string ## a distinct string type that
## is `tainted`:idx:. It is an alias for ## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not ## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode`` ## turned on. Use the ``-d:taintMode``
@ -1136,25 +1136,25 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
## use this instead of `=` for a `shallow copy`:idx:. The shallow copy ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
## only changes the semantics for sequences and strings (and types which ## only changes the semantics for sequences and strings (and types which
## contain those). Be careful with the changed semantics though! There ## contain those). Be careful with the changed semantics though! There
## is a reason why the default assignment does a deep copy of sequences ## is a reason why the default assignment does a deep copy of sequences
## and strings. ## and strings.
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} = proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`. ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
## This is an O(1) operation. ## This is an O(1) operation.
let xl = x.len let xl = x.len
shallowCopy(x[i], x[xl-1]) shallowCopy(x[i], x[xl-1])
setLen(x, xl-1) setLen(x, xl-1)
proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} = proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} =
## deletes the item at index `i` by moving ``x[i+1..]`` by one position. ## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
## This is an O(n) operation. ## This is an O(n) operation.
let xl = x.len let xl = x.len
for j in i..xl-2: shallowCopy(x[j], x[j+1]) for j in i..xl-2: shallowCopy(x[j], x[j+1])
setLen(x, xl-1) setLen(x, xl-1)
proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
## inserts `item` into `x` at position `i`. ## inserts `item` into `x` at position `i`.
let xl = x.len let xl = x.len
setLen(x, xl+1) setLen(x, xl+1)
@ -1233,7 +1233,7 @@ type # these work for most platforms:
## This is binary compatible to the type ``char**`` in *C*. The array's ## This is binary compatible to the type ``char**`` in *C*. The array's
## high value is large enough to disable bounds checking in practice. ## high value is large enough to disable bounds checking in practice.
## Use `cstringArrayToSeq` to convert it into a ``seq[string]``. ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
PFloat32* = ptr float32 ## an alias for ``ptr float32`` PFloat32* = ptr float32 ## an alias for ``ptr float32``
PFloat64* = ptr float64 ## an alias for ``ptr float64`` PFloat64* = ptr float64 ## an alias for ``ptr float64``
PInt64* = ptr int64 ## an alias for ``ptr int64`` PInt64* = ptr int64 ## an alias for ``ptr int64``
@ -1280,7 +1280,7 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
proc copy*(s: string, first = 0): string {. proc copy*(s: string, first = 0): string {.
magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.} magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.}
proc copy*(s: string, first, last: int): string {. proc copy*(s: string, first, last: int): string {.
magic: "CopyStrLast", importc: "copyStrLast", noSideEffect, magic: "CopyStrLast", importc: "copyStrLast", noSideEffect,
deprecated.} deprecated.}
## copies a slice of `s` into a new string and returns this new ## copies a slice of `s` into a new string and returns this new
## string. The bounds `first` and `last` denote the indices of ## string. The bounds `first` and `last` denote the indices of
@ -1358,7 +1358,7 @@ when not defined(nimrodVM):
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `createShared` to allocate from a shared heap. ## Use `createShared` to allocate from a shared heap.
cast[ptr T](alloc0(T.sizeof * size)) cast[ptr T](alloc0(T.sizeof * size))
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [],
benign.} benign.}
## grows or shrinks a given memory block. If p is **nil** then a new ## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least ## memory block is returned. In either way the block has at least
@ -1381,7 +1381,7 @@ when not defined(nimrodVM):
## ``realloc``. This procedure is dangerous! If one forgets to ## ``realloc``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed ## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen ## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted. ## or other memory may be corrupted.
## The freed memory must belong to its allocating thread! ## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap. ## Use `deallocShared` to deallocate from a shared heap.
proc free*[T](p: ptr T) {.inline, benign.} = proc free*[T](p: ptr T) {.inline, benign.} =
@ -1390,30 +1390,30 @@ when not defined(nimrodVM):
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is ## is not initialized, so reading from it before writing to it is
## undefined behaviour! ## undefined behaviour!
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline,
benign.} = benign.} =
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
## is not initialized, so reading from it before writing to it is ## is not initialized, so reading from it before writing to it is
## undefined behaviour! ## undefined behaviour!
cast[ptr T](allocShared(T.sizeof * size)) cast[ptr T](allocShared(T.sizeof * size))
proc allocShared0*(size: int): pointer {.noconv, rtl, benign.} proc allocShared0*(size: int): pointer {.noconv, rtl, benign.}
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``. ## containing zero, so it is somewhat safer than ``allocShared``.
proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``. ## ``resizeShared(block, 0)`` or ``freeShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``createSharedU``. ## containing zero, so it is somewhat safer than ``createSharedU``.
cast[ptr T](allocShared0(T.sizeof * size)) cast[ptr T](allocShared0(T.sizeof * size))
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl,
benign.} benign.}
## grows or shrinks a given memory block on the heap. If p is **nil** ## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at ## then a new memory block is returned. In either way the block has at
@ -1525,7 +1525,7 @@ const
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
## is the version of Nim as a string. ## is the version of Nim as a string.
{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion, {.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion,
NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].} NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].}
# GC interface: # GC interface:
@ -1805,7 +1805,7 @@ proc `==` *[I, T](x, y: array[I, T]): bool =
return return
result = true result = true
proc `@`*[T](a: openArray[T]): seq[T] = proc `@`*[T](a: openArray[T]): seq[T] =
## turns an openarray into a sequence. This is not as efficient as turning ## turns an openarray into a sequence. This is not as efficient as turning
## a fixed length array into a sequence as it always copies every element ## a fixed length array into a sequence as it always copies every element
## of `a`. ## of `a`.
@ -1853,7 +1853,7 @@ when not defined(NimrodVM):
else: else:
proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
asm """return `x`""" asm """return `x`"""
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
## Generic equals operator for sequences: relies on a equals operator for ## Generic equals operator for sequences: relies on a equals operator for
## the element type `T`. ## the element type `T`.
@ -1879,7 +1879,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
## for ``find(a, item) >= 0``. ## for ``find(a, item) >= 0``.
return find(a, item) >= 0 return find(a, item) >= 0
proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
## returns the last item of `s` and decreases ``s.len`` by one. This treats ## returns the last item of `s` and decreases ``s.len`` by one. This treats
## `s` as a stack and implements the common *pop* operation. ## `s` as a stack and implements the common *pop* operation.
var L = s.len-1 var L = s.len-1
@ -1941,7 +1941,7 @@ iterator fields*[T: tuple|object](x: T): RootObj {.
iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {. iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {.
magic: "Fields", noSideEffect.} magic: "Fields", noSideEffect.}
## iterates over every field of `x` and `y`. ## iterates over every field of `x` and `y`.
## Warning: This is really transforms the 'for' and unrolls the loop. ## Warning: This is really transforms the 'for' and unrolls the loop.
## The current implementation also has a bug that affects symbol binding ## The current implementation also has a bug that affects symbol binding
## in the loop body. ## in the loop body.
iterator fieldPairs*[T: tuple|object](x: T): RootObj {. iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
@ -1982,18 +1982,18 @@ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
a, b: expr] {. a, b: expr] {.
magic: "FieldPairs", noSideEffect.} magic: "FieldPairs", noSideEffect.}
## iterates over every field of `x` and `y`. ## iterates over every field of `x` and `y`.
## Warning: This really transforms the 'for' and unrolls the loop. ## Warning: This really transforms the 'for' and unrolls the loop.
## The current implementation also has a bug that affects symbol binding ## The current implementation also has a bug that affects symbol binding
## in the loop body. ## in the loop body.
proc `==`*[T: tuple|object](x, y: T): bool = proc `==`*[T: tuple|object](x, y: T): bool =
## generic ``==`` operator for tuples that is lifted from the components ## generic ``==`` operator for tuples that is lifted from the components
## of `x` and `y`. ## of `x` and `y`.
for a, b in fields(x, y): for a, b in fields(x, y):
if a != b: return false if a != b: return false
return true return true
proc `<=`*[T: tuple](x, y: T): bool = proc `<=`*[T: tuple](x, y: T): bool =
## generic ``<=`` operator for tuples that is lifted from the components ## generic ``<=`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`. ## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y): for a, b in fields(x, y):
@ -2002,7 +2002,7 @@ proc `<=`*[T: tuple](x, y: T): bool =
if c > 0: return false if c > 0: return false
return true return true
proc `<`*[T: tuple](x, y: T): bool = proc `<`*[T: tuple](x, y: T): bool =
## generic ``<`` operator for tuples that is lifted from the components ## generic ``<`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`. ## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y): for a, b in fields(x, y):
@ -2011,7 +2011,7 @@ proc `<`*[T: tuple](x, y: T): bool =
if c > 0: return false if c > 0: return false
return false return false
proc `$`*[T: tuple|object](x: T): string = proc `$`*[T: tuple|object](x: T): string =
## generic ``$`` operator for tuples that is lifted from the components ## generic ``$`` operator for tuples that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2021,13 +2021,13 @@ proc `$`*[T: tuple|object](x: T): string =
result = "(" result = "("
var firstElement = true var firstElement = true
for name, value in fieldPairs(x): for name, value in fieldPairs(x):
if not(firstElement): result.add(", ") if not firstElement: result.add(", ")
result.add(name) result.add(name)
result.add(": ") result.add(": ")
result.add($value) result.add($value)
firstElement = false firstElement = false
result.add(")") result.add(")")
proc collectionToString[T](x: T, b, e: string): string = proc collectionToString[T](x: T, b, e: string): string =
result = b result = b
var firstElement = true var firstElement = true
@ -2037,7 +2037,7 @@ proc collectionToString[T](x: T, b, e: string): string =
firstElement = false firstElement = false
result.add(e) result.add(e)
proc `$`*[T](x: set[T]): string = proc `$`*[T](x: set[T]): string =
## generic ``$`` operator for sets that is lifted from the components ## generic ``$`` operator for sets that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2045,7 +2045,7 @@ proc `$`*[T](x: set[T]): string =
## ${23, 45} == "{23, 45}" ## ${23, 45} == "{23, 45}"
collectionToString(x, "{", "}") collectionToString(x, "{", "}")
proc `$`*[T](x: seq[T]): string = proc `$`*[T](x: seq[T]): string =
## generic ``$`` operator for seqs that is lifted from the components ## generic ``$`` operator for seqs that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2056,7 +2056,7 @@ proc `$`*[T](x: seq[T]): string =
when false: when false:
# causes bootstrapping to fail as we use array of chars and cstring should # causes bootstrapping to fail as we use array of chars and cstring should
# match better ... # match better ...
proc `$`*[T, IDX](x: array[IDX, T]): string = proc `$`*[T, IDX](x: array[IDX, T]): string =
collectionToString(x, "[", "]") collectionToString(x, "[", "]")
# ----------------- GC interface --------------------------------------------- # ----------------- GC interface ---------------------------------------------
@ -2098,14 +2098,14 @@ when not defined(nimrodVM) and hostOS != "standalone":
proc GC_getStatistics*(): string {.rtl, benign.} proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful ## returns an informative string about the GC's activity. This may be useful
## for tweaking. ## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", benign.} proc GC_ref*(x: string) {.magic: "GCref", benign.}
## marks the object `x` as referenced, so that it will not be freed until ## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`, ## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`. ## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.} proc GC_unref*(x: string) {.magic: "GCunref", benign.}
@ -2141,19 +2141,19 @@ var
## application code should never set this hook! You better know what you ## application code should never set this hook! You better know what you
## do when setting this. If ``localRaiseHook`` returns false, the exception ## do when setting this. If ``localRaiseHook`` returns false, the exception
## is caught and does not propagate further through the call stack. ## is caught and does not propagate further through the call stack.
outOfMemHook*: proc () {.nimcall, tags: [], benign.} outOfMemHook*: proc () {.nimcall, tags: [], benign.}
## set this variable to provide a procedure that should be called ## set this variable to provide a procedure that should be called
## in case of an `out of memory`:idx: event. The standard handler ## in case of an `out of memory`:idx: event. The standard handler
## writes an error message and terminates the program. `outOfMemHook` can ## writes an error message and terminates the program. `outOfMemHook` can
## be used to raise an exception in case of OOM like so: ## be used to raise an exception in case of OOM like so:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## var gOutOfMem: ref EOutOfMemory ## var gOutOfMem: ref EOutOfMemory
## new(gOutOfMem) # need to be allocated *before* OOM really happened! ## new(gOutOfMem) # need to be allocated *before* OOM really happened!
## gOutOfMem.msg = "out of memory" ## gOutOfMem.msg = "out of memory"
## ##
## proc handleOOM() = ## proc handleOOM() =
## raise gOutOfMem ## raise gOutOfMem
## ##
@ -2210,7 +2210,7 @@ proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_ ## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_
## instead. ## instead.
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].} tags: [], raises: [].}
## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho`` ## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho``
## pretends to be free of side effects, so that it can be used for debugging ## pretends to be free of side effects, so that it can be used for debugging
@ -2262,7 +2262,7 @@ proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} =
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} = proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} =
if x < 0: -x else: x if x < 0: -x else: x
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} = proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} =
## returns the absolute value of `x`. If `x` is ``low(x)`` (that ## returns the absolute value of `x`. If `x` is ``low(x)`` (that
## is -MININT for its type), an overflow exception is thrown (if overflow ## is -MININT for its type), an overflow exception is thrown (if overflow
## checking is turned on). ## checking is turned on).
if x < 0: -x else: x if x < 0: -x else: x
@ -2318,14 +2318,14 @@ when not defined(JS): #and not defined(NimrodVM):
# we use binary mode in Windows: # we use binary mode in Windows:
setmode(fileno(c_stdin), O_BINARY) setmode(fileno(c_stdin), O_BINARY)
setmode(fileno(c_stdout), O_BINARY) setmode(fileno(c_stdout), O_BINARY)
when defined(endb): when defined(endb):
proc endbStep() proc endbStep()
# ----------------- IO Part ------------------------------------------------ # ----------------- IO Part ------------------------------------------------
when hostOS != "standalone": when hostOS != "standalone":
type type
CFile {.importc: "FILE", header: "<stdio.h>", CFile {.importc: "FILE", header: "<stdio.h>",
final, incompletestruct.} = object final, incompletestruct.} = object
File* = ptr CFile ## The type representing a file handle. File* = ptr CFile ## The type representing a file handle.
@ -2375,9 +2375,9 @@ when not defined(JS): #and not defined(NimrodVM):
## Creates a ``TFile`` from a `filehandle` with given `mode`. ## Creates a ``TFile`` from a `filehandle` with given `mode`.
## ##
## Default mode is readonly. Returns true iff the file could be opened. ## Default mode is readonly. Returns true iff the file could be opened.
proc open*(filename: string, proc open*(filename: string,
mode: FileMode = fmRead, bufSize: int = -1): File = mode: FileMode = fmRead, bufSize: int = -1): File =
## Opens a file named `filename` with given `mode`. ## Opens a file named `filename` with given `mode`.
## ##
## Default mode is readonly. Raises an ``IO`` exception if the file ## Default mode is readonly. Raises an ``IO`` exception if the file
@ -2387,7 +2387,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
tags: [], benign.} tags: [], benign.}
## reopens the file `f` with given `filename` and `mode`. This ## reopens the file `f` with given `filename` and `mode`. This
## is often used to redirect the `stdin`, `stdout` or `stderr` ## is often used to redirect the `stdin`, `stdout` or `stderr`
## file variables. ## file variables.
## ##
@ -2398,7 +2398,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc endOfFile*(f: File): bool {.tags: [], benign.} proc endOfFile*(f: File): bool {.tags: [], benign.}
## Returns true iff `f` is at the end. ## Returns true iff `f` is at the end.
proc readChar*(f: File): char {. proc readChar*(f: File): char {.
importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].} importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].}
## Reads a single character from the stream `f`. ## Reads a single character from the stream `f`.
@ -2411,7 +2411,7 @@ when not defined(JS): #and not defined(NimrodVM):
## ##
## Raises an IO exception in case of an error. It is an error if the ## Raises an IO exception in case of an error. It is an error if the
## current file position is not at the beginning of the file. ## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
## Opens a file named `filename` for reading. ## Opens a file named `filename` for reading.
## ##
@ -2440,8 +2440,8 @@ when not defined(JS): #and not defined(NimrodVM):
## reads a line of text from the file `f`. May throw an IO exception. ## reads a line of text from the file `f`. May throw an IO exception.
## A line of text may be delimited by ``CR``, ``LF`` or ## A line of text may be delimited by ``CR``, ``LF`` or
## ``CRLF``. The newline character(s) are not part of the returned string. ## ``CRLF``. The newline character(s) are not part of the returned string.
proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
benign.} benign.}
## reads a line of text from the file `f` into `line`. `line` must not be ## reads a line of text from the file `f` into `line`. `line` must not be
## ``nil``! May throw an IO exception. ## ``nil``! May throw an IO exception.
@ -2450,7 +2450,7 @@ when not defined(JS): #and not defined(NimrodVM):
## Returns ``false`` if the end of the file has been reached, ``true`` ## Returns ``false`` if the end of the file has been reached, ``true``
## otherwise. If ``false`` is returned `line` contains no new data. ## otherwise. If ``false`` is returned `line` contains no new data.
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
tags: [WriteIOEffect], benign.} tags: [WriteIOEffect], benign.}
## writes the values `x` to `f` and then writes "\n". ## writes the values `x` to `f` and then writes "\n".
## May throw an IO exception. ## May throw an IO exception.
@ -2544,11 +2544,11 @@ when not defined(JS): #and not defined(NimrodVM):
dealloc(a) dealloc(a)
when not defined(NimrodVM): when not defined(NimrodVM):
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
discardable, benign.} discardable, benign.}
## atomic increment of `memLoc`. Returns the value after the operation. ## atomic increment of `memLoc`. Returns the value after the operation.
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, proc atomicDec*(memLoc: var int, x: int = 1): int {.inline,
discardable, benign.} discardable, benign.}
## atomic decrement of `memLoc`. Returns the value after the operation. ## atomic decrement of `memLoc`. Returns the value after the operation.
@ -2562,7 +2562,7 @@ when not defined(JS): #and not defined(NimrodVM):
context: C_JmpBuf context: C_JmpBuf
hasRaiseAction: bool hasRaiseAction: bool
raiseAction: proc (e: ref Exception): bool {.closure.} raiseAction: proc (e: ref Exception): bool {.closure.}
when declared(initAllocator): when declared(initAllocator):
initAllocator() initAllocator()
when hasThreadSupport: when hasThreadSupport:
@ -2576,7 +2576,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc setControlCHook*(hook: proc () {.noconv.} not nil) proc setControlCHook*(hook: proc () {.noconv.} not nil)
## allows you to override the behaviour of your application when CTRL+C ## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported. ## is pressed. Only one such hook is supported.
proc writeStackTrace*() {.tags: [WriteIOEffect].} proc writeStackTrace*() {.tags: [WriteIOEffect].}
## writes the current stack trace to ``stderr``. This is only works ## writes the current stack trace to ``stderr``. This is only works
## for debug builds. ## for debug builds.
@ -2587,20 +2587,20 @@ when not defined(JS): #and not defined(NimrodVM):
proc getStackTrace*(e: ref Exception): string proc getStackTrace*(e: ref Exception): string
## gets the stack trace associated with `e`, which is the stack that ## gets the stack trace associated with `e`, which is the stack that
## lead to the ``raise`` statement. This only works for debug builds. ## lead to the ``raise`` statement. This only works for debug builds.
{.push stack_trace: off, profiler:off.} {.push stack_trace: off, profiler:off.}
when hostOS == "standalone": when hostOS == "standalone":
include "system/embedded" include "system/embedded"
else: else:
include "system/excpt" include "system/excpt"
include "system/chcks" include "system/chcks"
# we cannot compile this with stack tracing on # we cannot compile this with stack tracing on
# as it would recurse endlessly! # as it would recurse endlessly!
include "system/arithm" include "system/arithm"
{.pop.} # stack trace {.pop.} # stack trace
{.pop.} # stack trace {.pop.} # stack trace
when hostOS != "standalone" and not defined(NimrodVM): when hostOS != "standalone" and not defined(NimrodVM):
include "system/dyncalls" include "system/dyncalls"
when not defined(NimrodVM): when not defined(NimrodVM):
@ -2608,7 +2608,7 @@ when not defined(JS): #and not defined(NimrodVM):
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
var d: int var d: int
@ -2728,7 +2728,7 @@ when not defined(JS): #and not defined(NimrodVM):
## process(value) ## process(value)
## else: ## else:
## echo "Value too big!" ## echo "Value too big!"
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
## Hints the optimizer that `val` is likely going to be false. ## Hints the optimizer that `val` is likely going to be false.
## ##
@ -2742,7 +2742,7 @@ when not defined(JS): #and not defined(NimrodVM):
## echo "Value too big!" ## echo "Value too big!"
## else: ## else:
## process(value) ## process(value)
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw proc pointer of the closure `x`. This is ## retrieves the raw proc pointer of the closure `x`. This is
## useful for interfacing closures with C. ## useful for interfacing closures with C.
@ -2774,7 +2774,7 @@ elif defined(JS):
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1 proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1 proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1 proc getTotalMem(): int = return -1
@ -2797,7 +2797,7 @@ elif defined(JS):
if x == y: return 0 if x == y: return 0
if x < y: return -1 if x < y: return -1
return 1 return 1
when defined(nimffi): when defined(nimffi):
include "system/sysio" include "system/sysio"
@ -2831,14 +2831,14 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
# cut down: # cut down:
setLen(s, newLen) setLen(s, newLen)
# fill the hole: # fill the hole:
for i in 0 .. <b.len: s[i+a] = b[i] for i in 0 .. <b.len: s[i+a] = b[i]
when hostOS != "standalone": when hostOS != "standalone":
proc `[]`*(s: string, x: Slice[int]): string {.inline.} = proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
## slice operation for strings. Negative indexes are supported. ## slice operation for strings. Negative indexes are supported.
result = s.substr(x.a-|s, x.b-|s) result = s.substr(x.a-|s, x.b-|s)
proc `[]=`*(s: var string, x: Slice[int], b: string) = proc `[]=`*(s: var string, x: Slice[int], b: string) =
## slice assignment for strings. Negative indexes are supported. If ## slice assignment for strings. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed: ## by `x`, a `splice`:idx: is performed:
@ -2880,7 +2880,7 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
newSeq(result, L) newSeq(result, L)
var j = x.a var j = x.a
for i in 0.. <L: for i in 0.. <L:
result[i] = a[j] result[i] = a[j]
inc(j) inc(j)
@ -2890,23 +2890,23 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
if L == b.len: if L == b.len:
var j = x.a var j = x.a
for i in 0 .. <L: for i in 0 .. <L:
a[j] = b[i] a[j] = b[i]
inc(j) inc(j)
else: else:
sysFatal(RangeError, "different lengths for slice assignment") sysFatal(RangeError, "different lengths for slice assignment")
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
## slice operation for sequences. Negative indexes are supported. ## slice operation for sequences. Negative indexes are supported.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
newSeq(result, L) newSeq(result, L)
for i in 0.. <L: result[i] = s[i + a] for i in 0.. <L: result[i] = s[i + a]
proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
## slice assignment for sequences. Negative indexes are supported. If ## slice assignment for sequences. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed. ## by `x`, a `splice`:idx: is performed.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
if L == b.len: if L == b.len:
@ -2937,7 +2937,7 @@ proc staticExec*(command: string, input = ""): string {.
## to the executed program. ## to the executed program.
## ##
## .. code-block:: nim ## .. code-block:: nim
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v") ## "\nCompiled on " & staticExec("uname -v")
## ##
## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
@ -2979,7 +2979,7 @@ proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful ## converts the AST of `x` into a string representation. This is very useful
## for debugging. ## for debugging.
proc instantiationInfo*(index = -1, fullPaths = false): tuple[ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.} filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.}
## provides access to the compiler's instantiation stack line information. ## provides access to the compiler's instantiation stack line information.
@ -3090,16 +3090,16 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
## Sets an assertion failure handler that will intercept any assert ## Sets an assertion failure handler that will intercept any assert
## statements following `onFailedAssert` in the current lexical scope. ## statements following `onFailedAssert` in the current lexical scope.
## Can be defined multiple times in a single function. ## Can be defined multiple times in a single function.
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## proc example(x: int): TErrorCode = ## proc example(x: int): TErrorCode =
## onFailedAssert(msg): ## onFailedAssert(msg):
## log msg ## log msg
## return E_FAIL ## return E_FAIL
## ##
## assert(...) ## assert(...)
## ##
## onFailedAssert(msg): ## onFailedAssert(msg):
## raise newException(EMyException, msg) ## raise newException(EMyException, msg)
## ##
@ -3111,7 +3111,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
## perform deep copies of `s`. This is only useful for optimization ## perform deep copies of `s`. This is only useful for optimization
## purposes. ## purposes.
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s) var s = cast[PGenericSeq](s)
@ -3119,7 +3119,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
proc shallow*(s: var string) {.noSideEffect, inline.} = proc shallow*(s: var string) {.noSideEffect, inline.} =
## marks a string `s` as `shallow`:idx:. Subsequent assignments will not ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not
## perform deep copies of `s`. This is only useful for optimization ## perform deep copies of `s`. This is only useful for optimization
## purposes. ## purposes.
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s) var s = cast[PGenericSeq](s)
@ -3141,13 +3141,13 @@ else:
when false: when false:
template eval*(blk: stmt): stmt = template eval*(blk: stmt): stmt =
## executes a block of code at compile time just as if it was a macro ## executes a block of code at compile time just as if it was a macro
## optionally, the block can return an AST tree that will replace the ## optionally, the block can return an AST tree that will replace the
## eval expression ## eval expression
macro payload: stmt {.gensym.} = blk macro payload: stmt {.gensym.} = blk
payload() payload()
when hostOS != "standalone": when hostOS != "standalone":
proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} =
## inserts `item` into `x` at position `i`. ## inserts `item` into `x` at position `i`.
var xl = x.len var xl = x.len
setLen(x, xl+item.len) setLen(x, xl+item.len)

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -48,7 +48,7 @@ type
TWalkOp = enum TWalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waCollectWhite #, waDebug waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
@ -61,9 +61,9 @@ type
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size maxStackSize: int # max stack size
maxStackCells: int # max stack cells in ``decStack`` maxStackCells: int # max stack cells in ``decStack``
cycleTableSize: int # max entries in cycle table cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
@ -88,11 +88,11 @@ var
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -163,7 +163,7 @@ when hasThreadSupport and hasSharedHeap:
template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement) template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
else: else:
template `--`(x: expr): expr = template `--`(x: expr): expr =
dec(x, rcIncrement) dec(x, rcIncrement)
x <% rcIncrement x <% rcIncrement
template `++`(x: expr): stmt = inc(x, rcIncrement) template `++`(x: expr): stmt = inc(x, rcIncrement)
@ -181,7 +181,7 @@ proc prepareDealloc(cell: PCell) =
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries! # we MUST access gch as a global here, because this crosses DLL boundaries!
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
@ -211,7 +211,7 @@ proc decRef(c: PCell) {.inline.} =
rtlAddCycleRoot(c) rtlAddCycleRoot(c)
#writeCell("decRef", c) #writeCell("decRef", c)
proc incRef(c: PCell) {.inline.} = proc incRef(c: PCell) {.inline.} =
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
c.refcount = c.refcount +% rcIncrement c.refcount = c.refcount +% rcIncrement
# and not colorMask # and not colorMask
@ -246,12 +246,12 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
dest[] = src dest[] = src
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc if it is known at compile time that no # the code generator calls this proc if it is known at compile time that no
# cycle is possible. # cycle is possible.
if src != nil: if src != nil:
var c = usrToCell(src) var c = usrToCell(src)
++c.refcount ++c.refcount
if dest[] != nil: if dest[] != nil:
var c = usrToCell(dest[]) var c = usrToCell(dest[])
if --c.refcount: if --c.refcount:
rtlAddZCT(c) rtlAddZCT(c)
@ -269,7 +269,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[])) if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
else: else:
# can't be an interior pointer if it's a stack location! # can't be an interior pointer if it's a stack location!
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil, gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
"stack loc AND interior pointer") "stack loc AND interior pointer")
dest[] = src dest[] = src
@ -321,7 +321,7 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables" echo "[GC] cannot register global variable; too many global variables"
quit 1 quit 1
proc cellsetReset(s: var TCellSet) = proc cellsetReset(s: var TCellSet) =
deinit(s) deinit(s)
init(s) init(s)
@ -336,7 +336,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}: if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op) doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
else: else:
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
n.sons[i].typ, op) n.sons[i].typ, op)
else: else:
forAllSlotsAux(dest, n.sons[i], op) forAllSlotsAux(dest, n.sons[i], op)
@ -384,7 +384,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck # In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down. # out of this small linear search so that ``newObj`` is not slowed down.
# #
# Slots to try cache hit # Slots to try cache hit
# 1 32% # 1 32%
# 4 59% # 4 59%
@ -481,7 +481,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@ -510,7 +510,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
@ -522,7 +522,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
@ -536,7 +536,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
writeCell("growObj new cell", res) writeCell("growObj new cell", res)
gcTrace(ol, csZctFreed) gcTrace(ol, csZctFreed)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc") sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1: if ol.refcount shr rcShift <=% 1:
# free immediately to save space: # free immediately to save space:
@ -580,7 +580,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
prepareDealloc(c) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c) when logGC: writeCell("cycle collector dealloc cell", c)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "free cyclic cell") sysAssert(allocInv(gch.region), "free cyclic cell")
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
@ -767,7 +767,7 @@ proc collectCycles(gch: var TGcHeap) =
gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots" gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell" gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
if canBeCycleRoot(c): if canBeCycleRoot(c):
#if c notin gch.cycleRoots: #if c notin gch.cycleRoots:
inc cycleRootsLen inc cycleRootsLen
incl(gch.cycleRoots, c) incl(gch.cycleRoots, c)
gcAssert c.typ != nil, "addBackStackRoots 2" gcAssert c.typ != nil, "addBackStackRoots 2"
@ -794,12 +794,12 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
while it != nil: while it != nil:
# mark registers: # mark registers:
for i in 0 .. high(it.registers): gcMark(gch, it.registers[i]) for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
var sp = cast[TAddress](it.stackBottom) var sp = cast[TAddress](it.stackBottom)
var max = cast[TAddress](it.stackTop) var max = cast[TAddress](it.stackTop)
@ -933,7 +933,7 @@ else:
while sp <=% max: while sp <=% max:
gcMark(gch, cast[PPointer](sp)[]) gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer) sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark) forEachStackSlot(gch, gcMark)
@ -946,13 +946,13 @@ when useMarkForDebug or useBackupGc:
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var TGcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do # Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to # deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small. # avoid a deep stack, we move objects to keep the ZCT small.
# This is performance critical! # This is performance critical!
const workPackage = 100 const workPackage = 100
var L = addr(gch.zct.len) var L = addr(gch.zct.len)
when withRealTime: when withRealTime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: TTicks
@ -962,15 +962,15 @@ proc collectZCT(gch: var TGcHeap): bool =
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
# remove from ZCT: # remove from ZCT:
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT") gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
c.refcount = c.refcount and not ZctFlag c.refcount = c.refcount and not ZctFlag
gch.zct.d[0] = gch.zct.d[L[] - 1] gch.zct.d[0] = gch.zct.d[L[] - 1]
dec(L[]) dec(L[])
when withRealTime: dec steps when withRealTime: dec steps
if c.refcount <% rcIncrement: if c.refcount <% rcIncrement:
# It may have a RC > 0, if it is in the hardware stack or # It may have a RC > 0, if it is in the hardware stack or
# it has not been removed yet from the ZCT. This is because # it has not been removed yet from the ZCT. This is because
# ``incref`` does not bother to remove the cell from the ZCT # ``incref`` does not bother to remove the cell from the ZCT
# as this might be too slow. # as this might be too slow.
# In any case, it should be removed from the ZCT. But not # In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
@ -983,7 +983,7 @@ proc collectZCT(gch: var TGcHeap): bool =
# access invalid memory. This is done by prepareDealloc(): # access invalid memory. This is done by prepareDealloc():
prepareDealloc(c) prepareDealloc(c)
forAllChildren(c, waZctDecRef) forAllChildren(c, waZctDecRef)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc") sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
@ -994,7 +994,7 @@ proc collectZCT(gch: var TGcHeap): bool =
steps = workPackage steps = workPackage
if gch.maxPause > 0: if gch.maxPause > 0:
let duration = getticks() - t0 let duration = getticks() - t0
# the GC's measuring is not accurate and needs some cleanup actions # the GC's measuring is not accurate and needs some cleanup actions
# (stack unmarking), so subtract some short amount of time in # (stack unmarking), so subtract some short amount of time in
# order to miss deadlines less often: # order to miss deadlines less often:
if duration >= gch.maxPause - 50_000: if duration >= gch.maxPause - 50_000:
@ -1017,7 +1017,7 @@ proc collectCTBody(gch: var TGcHeap) =
when withRealTime: when withRealTime:
let t0 = getticks() let t0 = getticks()
sysAssert(allocInv(gch.region), "collectCT: begin") sysAssert(allocInv(gch.region), "collectCT: begin")
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
sysAssert(gch.decStack.len == 0, "collectCT") sysAssert(gch.decStack.len == 0, "collectCT")
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
@ -1036,7 +1036,7 @@ proc collectCTBody(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
unmarkStackAndRegisters(gch) unmarkStackAndRegisters(gch)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
when withRealTime: when withRealTime:
let duration = getticks() - t0 let duration = getticks() - t0
gch.stat.maxPause = max(gch.stat.maxPause, duration) gch.stat.maxPause = max(gch.stat.maxPause, duration)
@ -1050,8 +1050,12 @@ when useMarkForDebug or useBackupGc:
markGlobals(gch) markGlobals(gch)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and # stackMarkCosts prevents some pathological behaviour: Stack marking
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and # becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack.
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
if (gch.zct.len >= stackMarkCosts or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
when useMarkForDebug: when useMarkForDebug:
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
@ -1070,7 +1074,7 @@ when withRealTime:
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
strongAdvice: strongAdvice:
collectCTBody(gch) collectCTBody(gch)
release(gch) release(gch)
@ -1078,13 +1082,13 @@ when withRealTime:
proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice) proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
when not defined(useNimRtl): when not defined(useNimRtl):
proc GC_disable() = proc GC_disable() =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
discard atomicInc(gch.recGcLock, 1) discard atomicInc(gch.recGcLock, 1)
else: else:
inc(gch.recGcLock) inc(gch.recGcLock)
proc GC_enable() = proc GC_enable() =
if gch.recGcLock > 0: if gch.recGcLock > 0:
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
discard atomicDec(gch.recGcLock, 1) discard atomicDec(gch.recGcLock, 1)
else: else:

View file

@ -53,7 +53,6 @@ Bugs
- VM: Pegs do not work at compile-time - VM: Pegs do not work at compile-time
- VM: ptr/ref T cannot work in general - VM: ptr/ref T cannot work in general
- scopes are still broken for generic instantiation! - scopes are still broken for generic instantiation!
- compilation of niminst takes way too long. looks like a regression
- blocks can "export" an identifier but the CCG generates {} for them ... - blocks can "export" an identifier but the CCG generates {} for them ...