From dfb020b174b6f3489294d9ec096de3cf9d6b5944 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 12 Nov 2019 15:05:36 +0100 Subject: .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 --- compiler/ast.nim | 3 ++- compiler/commands.nim | 22 +++++++++++----------- compiler/condsyms.nim | 1 + compiler/injectdestructors.nim | 18 +++++++++++++++--- compiler/liftdestructors.nim | 30 ++++++++++++++++++++---------- compiler/pragmas.nim | 7 +++++-- compiler/renderer.nim | 2 ++ compiler/wordrecg.nim | 4 ++-- lib/core/allocators.nim | 4 ++-- lib/core/runtime_v2.nim | 2 +- lib/pure/collections/lists.nim | 11 +++++++---- tests/destructor/tlists.nim | 38 ++++++++++++++++++++++++++++++++++++++ 12 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 tests/destructor/tlists.nim 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..(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 -- cgit 1.4.1-2-gfad0