diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2019-11-12 15:05:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-12 15:05:36 +0100 |
commit | dfb020b174b6f3489294d9ec096de3cf9d6b5944 (patch) | |
tree | 30a5ef1faaa611c5de2d092d03c5cb12df0741b5 | |
parent | 7e689873e203620c3a14f1697129cd0e3fd548e4 (diff) | |
download | Nim-dfb020b174b6f3489294d9ec096de3cf9d6b5944.tar.gz |
.cursor implementation (#12637)
* cursors: first implementation * added currently failing test * .cursor works for doubly linked lists * make -d:useMalloc work again * added code to nil out refs in a destructor * it's now called --gc:arc * renderer.nim: render nkBreakState properly * make simple closure iterators work without leaking
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/commands.nim | 22 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/injectdestructors.nim | 18 | ||||
-rw-r--r-- | compiler/liftdestructors.nim | 30 | ||||
-rw-r--r-- | compiler/pragmas.nim | 7 | ||||
-rw-r--r-- | compiler/renderer.nim | 2 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 | ||||
-rw-r--r-- | lib/core/allocators.nim | 4 | ||||
-rw-r--r-- | lib/core/runtime_v2.nim | 2 | ||||
-rw-r--r-- | lib/pure/collections/lists.nim | 11 | ||||
-rw-r--r-- | tests/destructor/tlists.nim | 38 |
12 files changed, 106 insertions, 36 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 27db97f0c..06041eaa7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -228,7 +228,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # already 38 flags! + TSymFlag* = enum # 39 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -284,6 +284,7 @@ type # variable is generated closure environment; requires early # destruction for --newruntime. sfTemplateParam # symbol is a template parameter + sfCursor # variable/field is a cursor, see RFC 177 for details TSymFlags* = set[TSymFlag] diff --git a/compiler/commands.nim b/compiler/commands.nim index 12526e6c7..b0adaae83 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -224,15 +224,15 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo case switch.normalize of "gc": case arg.normalize - of "boehm": result = conf.selectedGC == gcBoehm - of "refc": result = conf.selectedGC == gcRefc - of "v2": result = false + of "boehm": result = conf.selectedGC == gcBoehm + of "refc": result = conf.selectedGC == gcRefc + of "v2": result = false of "markandsweep": result = conf.selectedGC == gcMarkAndSweep of "generational": result = false - of "destructors": result = conf.selectedGC == gcDestructors - of "hooks": result = conf.selectedGC == gcHooks - of "go": result = conf.selectedGC == gcGo - of "none": result = conf.selectedGC == gcNone + of "destructors", "arc": result = conf.selectedGC == gcDestructors + of "hooks": result = conf.selectedGC == gcHooks + of "go": result = conf.selectedGC == gcGo + of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": @@ -244,9 +244,9 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "verbosity": result = $conf.verbosity == arg of "app": case arg.normalize - of "gui": result = contains(conf.globalOptions, optGenGuiApp) - of "console": result = not contains(conf.globalOptions, optGenGuiApp) - of "lib": result = contains(conf.globalOptions, optGenDynLib) and + of "gui": result = contains(conf.globalOptions, optGenGuiApp) + of "console": result = not contains(conf.globalOptions, optGenGuiApp) + of "lib": result = contains(conf.globalOptions, optGenDynLib) and not contains(conf.globalOptions, optGenGuiApp) of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and not contains(conf.globalOptions, optGenGuiApp) @@ -453,7 +453,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "markandsweep": conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") - of "destructors": + of "destructors", "arc": conf.selectedGC = gcDestructors defineSymbol(conf.symbols, "gcdestructors") incl conf.globalOptions, optSeqDestructors diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9e2dc2532..ae23fec3f 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -101,3 +101,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimFixedForwardGeneric") defineSymbol("nimnomagic64") defineSymbol("nimNewShiftOps") + defineSymbol("nimHasCursor") diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b6dbf797c..96b4e5240 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -418,6 +418,17 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = else: result = p(arg, c) +proc isCursor(n: PNode): bool = + case n.kind + of nkSym: + result = sfCursor in n.sym.flags + of nkDotExpr: + result = sfCursor in n[1].sym.flags + of nkCheckedFieldExpr: + result = isCursor(n[0]) + else: + result = false + proc p(n: PNode; c: var Con): PNode = case n.kind of nkCallKinds: @@ -459,7 +470,7 @@ proc p(n: PNode; c: var Con): PNode = if it.kind == nkVarTuple and hasDestructor(ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c) - elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): + elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]): for j in 0..<it.len-2: let v = it[j] if v.kind == nkSym: @@ -483,7 +494,8 @@ proc p(n: PNode; c: var Con): PNode = v.add itCopy result.add v of nkAsgn, nkFastAsgn: - if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}: + if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and + not isCursor(n[0]): # rule (self-assignment-removal): if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym: result = newNodeI(nkEmpty, n.info) @@ -515,7 +527,7 @@ proc p(n: PNode; c: var Con): PNode = of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, - nkPragma, nkCommentStmt, nkBreakStmt: + nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState: result = n of nkWhileStmt: result = copyNode(n) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 94cad9bad..3787fe6a7 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -52,11 +52,24 @@ proc dotField(x: PNode, f: PSym): PNode = result.sons[1] = newSymNode(f, x.info) result.typ = f.typ +proc newAsgnStmt(le, ri: PNode): PNode = + result = newNodeI(nkAsgn, le.info, 2) + result.sons[0] = le + result.sons[1] = ri + +proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = + if c.kind != attachedDestructor: + body.add newAsgnStmt(x, y) + proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = case n.kind of nkSym: let f = n.sym - fillBody(c, f.typ, body, x.dotField(f), y.dotField(f)) + if sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and + c.g.config.selectedGC == gcDestructors: + defaultOp(c, f.typ, body, x.dotField(f), y.dotField(f)) + else: + fillBody(c, f.typ, body, x.dotField(f), y.dotField(f)) of nkNilLit: discard of nkRecCase: if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}: @@ -113,11 +126,6 @@ proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode = result.add genAddr(g, x) result.add y -proc newAsgnStmt(le, ri: PNode): PNode = - result = newNodeI(nkAsgn, le.info, 2) - result.sons[0] = le - result.sons[1] = ri - proc newOpCall(op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ.sons[0]) result.add(newSymNode(op)) @@ -236,10 +244,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add newDeepCopyCall(op, x, y) result = true -proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = - if c.kind != attachedDestructor: - body.add newAsgnStmt(x, y) - proc addVar(father, v, value: PNode) = var vpart = newNodeI(nkIdentDefs, v.info, 3) vpart.sons[0] = v @@ -393,6 +397,9 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: + when false: + # XXX investigate if this is necessary: + actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") @@ -419,6 +426,9 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: + when false: + # XXX investigate if this is necessary: + actions.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ)) body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b1eecc50e..205ed4309 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -62,11 +62,11 @@ const wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wBorrow, wGcSafe, wPartial, wExplain, wPackage} fieldPragmas* = declPragmas + { - wGuard, wBitsize} - {wExportNims, wNodecl} # why exclude these? + wGuard, wBitsize, wCursor} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore} @@ -833,6 +833,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wVolatile: noVal(c, it) incl(sym.flags, sfVolatile) + of wCursor: + noVal(c, it) + incl(sym.flags, sfCursor) of wRegister: noVal(c, it) incl(sym.flags, sfRegister) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 2fbe01ce2..dc6ff93c0 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1526,6 +1526,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkBreakState: put(g, tkTuple, "breakstate") + if renderIds in g.flags: + gsons(g, n, c, 0) of nkTypeClassTy: gTypeClassTy(g, n) else: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 4650f22ec..fa18d4f7f 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -35,7 +35,7 @@ type wColon, wColonColon, wEquals, wDot, wDotDot, wStar, wMinus, wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks, - wIntDefine, wStrDefine, wBoolDefine + wIntDefine, wStrDefine, wBoolDefine, wCursor, wImmediate, wConstructor, wDestructor, wDelegator, wOverride, wImportCpp, wImportObjC, @@ -121,7 +121,7 @@ const ":", "::", "=", ".", "..", "*", "-", "magic", "thread", "final", "profiler", "memtracker", "objchecks", - "intdefine", "strdefine", "booldefine", + "intdefine", "strdefine", "booldefine", "cursor", "immediate", "constructor", "destructor", "delegator", "override", "importcpp", "importobjc", diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim index 25df59b63..43aae0111 100644 --- a/lib/core/allocators.nim +++ b/lib/core/allocators.nim @@ -43,7 +43,7 @@ proc getLocalAllocator*(): Allocator = result = addr allocatorStorage result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall, raises: [].} = when defined(useMalloc) and not defined(nimscript): - result = c_malloc(size) + result = c_malloc(cuint size) # XXX do we need this? nimZeroMem(result, size) else: @@ -57,7 +57,7 @@ proc getLocalAllocator*(): Allocator = inc a.deallocCount result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall, raises: [].} = when defined(useMalloc) and not defined(nimscript): - result = c_realloc(p, newSize) + result = c_realloc(p, cuint newSize) else: result = system.realloc(p, newSize) nimZeroMem(result +! oldSize, newSize - oldSize) diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim index aa2511ce4..d18f91f6f 100644 --- a/lib/core/runtime_v2.nim +++ b/lib/core/runtime_v2.nim @@ -47,7 +47,7 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = when defined(nimscript): discard elif defined(useMalloc): - var orig = c_malloc(s) + var orig = c_malloc(cuint s) nimZeroMem(orig, s) result = orig +! sizeof(RefHeader) else: diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index b95cfffc0..bd22949bf 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -76,13 +76,16 @@ when not defined(nimhygiene): {.pragma: dirty.} +when not defined(nimHasCursor): + {.pragma: cursor.} + type DoublyLinkedNodeObj*[T] = object ## \ ## A node a doubly linked list consists of. ## ## It consists of a `value` field, and pointers to `next` and `prev`. next*: <//>(ref DoublyLinkedNodeObj[T]) - prev*: ref DoublyLinkedNodeObj[T] + prev* {.cursor.}: ref DoublyLinkedNodeObj[T] value*: T DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T] @@ -100,7 +103,7 @@ type ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create ## a new empty list. head*: <//>(SinglyLinkedNode[T]) - tail*: SinglyLinkedNode[T] + tail* {.cursor.}: SinglyLinkedNode[T] DoublyLinkedList*[T] = object ## \ ## A doubly linked list. @@ -108,7 +111,7 @@ type ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create ## a new empty list. head*: <//>(DoublyLinkedNode[T]) - tail*: DoublyLinkedNode[T] + tail* {.cursor.}: DoublyLinkedNode[T] SinglyLinkedRing*[T] = object ## \ ## A singly linked ring. @@ -116,7 +119,7 @@ type ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create ## a new empty ring. head*: <//>(SinglyLinkedNode[T]) - tail*: SinglyLinkedNode[T] + tail* {.cursor.}: SinglyLinkedNode[T] DoublyLinkedRing*[T] = object ## \ ## A doubly linked ring. diff --git a/tests/destructor/tlists.nim b/tests/destructor/tlists.nim new file mode 100644 index 000000000..7a81e602e --- /dev/null +++ b/tests/destructor/tlists.nim @@ -0,0 +1,38 @@ +discard """ + output: '''Success +@["a", "b", "c"] +0''' + cmd: '''nim c --gc:destructors $file''' +""" + +import os +import math +import lists +import strutils + +proc mkleak() = + # allocate 1 MB via linked lists + let numberOfLists = 100 + for i in countUp(1, numberOfLists): + var leakList = initDoublyLinkedList[string]() + let numberOfLeaks = 5000 + for j in countUp(1, numberOfLeaks): + leakList.append(newString(200)) + +proc mkManyLeaks() = + for i in 0..0: + mkleak() + echo "Success" + +iterator foobar(c: string): seq[string] {.closure.} = + yield @["a", "b", c] + +proc tsimpleClosureIterator = + var myc = "c" + for it in foobar(myc): + echo it + +let startMem = getOccupiedMem() +mkManyLeaks() +tsimpleClosureIterator() +echo getOccupiedMem() - startMem |