diff options
-rw-r--r-- | compiler/ast.nim | 4 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 50 | ||||
-rw-r--r-- | compiler/semcall.nim | 17 | ||||
-rw-r--r-- | compiler/seminst.nim | 25 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 21 | ||||
-rw-r--r-- | compiler/vm.nim | 32 | ||||
-rw-r--r-- | compiler/vmgen.nim | 23 | ||||
-rw-r--r-- | lib/system/gc.nim | 36 | ||||
-rw-r--r-- | tests/gc/closureleak.nim | 4 | ||||
-rw-r--r-- | tests/gc/cyclecollector.nim | 21 | ||||
-rw-r--r-- | tests/gc/gctest.nim | 3 | ||||
-rw-r--r-- | tests/metatype/tstaticparams.nim | 21 | ||||
-rw-r--r-- | tests/static/tstaticparammacro.nim | 22 | ||||
-rw-r--r-- | tests/testament/categories.nim | 13 | ||||
-rw-r--r-- | web/question.txt | 1 |
15 files changed, 231 insertions, 62 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 883b68d71..a071060d4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1341,6 +1341,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) +proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType = + result = if t != nil: t.skipTypes(kinds) + else: nil + proc isGCedMem*(t: PType): bool {.inline.} = result = t.kind in {tyString, tyRef, tySequence} or t.kind == tyProc and t.callConv == ccClosure diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 78cc691c0..ecb898d8a 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -25,16 +25,22 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = if ctx.instLines: result.info = b.info proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = + template handleParam(param) = + let x = param + if x.kind == nkArgList: + for y in items(x): result.add(y) + else: + result.add copyTree(x) + case templ.kind of nkSym: var s = templ.sym if s.owner.id == c.owner.id: - if s.kind == skParam: - let x = actual.sons[s.position] - if x.kind == nkArgList: - for y in items(x): result.add(y) - else: - result.add copyTree(x) + case s.kind + of skParam: + handleParam actual.sons[s.position] + of skGenericParam: + handleParam actual.sons[s.owner.typ.len + s.position - 1] else: internalAssert sfGenSym in s.flags var x = PSym(idTableGet(c.mapping, s)) @@ -56,21 +62,31 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar - var a: int - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - a = sonsLen(n) - else: a = 0 - var f = s.typ.sonsLen - if a > f: globalError(n.info, errWrongNumberOfArguments) + var totalParams = case n.kind + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len + else: 0 + + var + genericParams = s.ast[genericParamsPos].len + expectedRegularParams = <s.typ.len + givenRegularParams = totalParams - genericParams + + if totalParams > expectedRegularParams + genericParams: + globalError(n.info, errWrongNumberOfArguments) result = newNodeI(nkArgList, n.info) - for i in countup(1, f - 1): - var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) - if arg == nil or arg.kind == nkEmpty: + for i in 1 .. givenRegularParams: + result.addSon n.sons[i] + + for i in givenRegularParams+1 .. expectedRegularParams: + let default = s.typ.n.sons[i].sym.ast + if default.kind == nkEmpty: localError(n.info, errWrongNumberOfArguments) - addSon(result, arg) + result.addSon default.copyTree + for i in 1 .. genericParams: + result.addSon n.sons[givenRegularParams + i] + var evalTemplateCounter* = 0 # to prevent endless recursion in templates instantiation diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a712cc195..961c61c57 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -283,8 +283,21 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: result.typ = newTypeS(x.fauxMatch, c) return - if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + let gp = finalCallee.ast.sons[genericParamsPos] + if gp.kind != nkEmpty: + if x.calleeSym.kind notin {skMacro, skTemplate}: + finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + else: + # For macros and templates, the resolved generic params + # are added as normal params. + for s in instantiateGenericParamList(c, gp, x.bindings): + case s.kind + of skConst: + x.call.add s.ast + of skType: + x.call.add newSymNode(s, n.info) + else: + internalAssert false result = x.call instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 81a4465c5..dd60e0881 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,14 +10,10 @@ # This module implements the instantiation of generic procs. # included from sem.nim -proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: - internalError(n.info, "instantiateGenericParamList; no generic params") - newSeq(entry.concreteTypes, n.len) +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = + internalAssert n.kind == nkGenericParams for i, a in n.pairs: - if a.kind != nkSym: - internalError(a.info, "instantiateGenericParamList; no symbol") + internalAssert a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue @@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = ReplaceTypeVarsT(cl, t) s.typ = t if t.kind == tyStatic: s.ast = t.n - addDecl(c, s) - entry.concreteTypes[i] = t + yield s proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: @@ -196,7 +191,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: - if fn.kind in {skTemplate, skMacro}: return fn + internalAssert fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -213,12 +208,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(result) openScope(c) - internalAssert n.sons[genericParamsPos].kind != nkEmpty + let gp = n.sons[genericParamsPos] + internalAssert gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new entry.sym = result - instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + newSeq(entry.concreteTypes, gp.len) + var i = 0 + for s in instantiateGenericParamList(c, gp, pt): + addDecl(c, s) + entry.concreteTypes[i] = s.typ + inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) n.sons[genericParamsPos] = ast.emptyNode diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0182cb555..721f7e318 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1004,15 +1004,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, x, a) # check if it fits of tyStatic: - if aOrig.kind == tyStatic: - result = typeRel(c, f.lastSon, a) - if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): - result = isNone - if result != isNone: put(c.bindings, f, aOrig) + let prev = PType(idTableGet(c.bindings, f)) + if prev == nil: + if aOrig.kind == tyStatic: + result = typeRel(c, f.lastSon, a) + if result != isNone and f.n != nil: + if not exprStructuralEquivalent(f.n, aOrig.n): + result = isNone + if result != isNone: put(c.bindings, f, aOrig) + else: + result = isNone else: - result = isNone - + result = typeRel(c, prev, aOrig) + of tyTypeDesc: var prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -1051,6 +1055,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFromExpr: # fix the expression, so it contains the already instantiated types + if f.n == nil: return isGeneric let reevaluated = tryResolvingStaticExpr(c, f.n) case reevaluated.typ.kind of tyTypeDesc: diff --git a/compiler/vm.nim b/compiler/vm.nim index ad0d3b0a1..4072ed765 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1417,12 +1417,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym, n: PNode) = discard evalConstExprAux(module, nil, n, emStaticStmt) -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - result = canonValue(result) - result.flags.incl nfIsRef - result.typ = x.typ +proc setupMacroParam(x: PNode, typ: PType): TFullReg = + case typ.kind + of tyStatic: + putIntoReg(result, x) + of tyTypeDesc: + putIntoReg(result, x) + else: + result.kind = rkNode + var n = x + if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1] + n = n.canonValue + n.flags.incl nfIsRef + n.typ = x.typ + result.node = n var evalMacroCounter: int @@ -1442,6 +1450,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = c.callsite = nOrig let start = genProc(c, sym) + # c.echoCode start var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset @@ -1457,9 +1466,14 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) # setup parameters: - for i in 1 .. < min(tos.slots.len, L): - tos.slots[i].kind = rkNode - tos.slots[i].node = setupMacroParam(n.sons[i]) + for i in 1.. <sym.typ.len: + tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i]) + + let gp = sym.ast[genericParamsPos] + for i in 0 .. <gp.len: + let idx = sym.typ.len + i + tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ) + # temporary storage: #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9a3fc260a..8444af7ba 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -164,7 +164,8 @@ proc getSlotKind(t: PType): TSlotKind = const HighRegisterPressure = 40 -proc getTemp(c: PCtx; typ: PType): TRegister = +proc getTemp(c: PCtx; tt: PType): TRegister = + let typ = tt.safeSkipTypes({tyStatic}) let c = c.prc # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen @@ -685,7 +686,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.gABx(n, opc, 0, genType(c, n.typ)) - c.gABx(n, opc, 0, genType(c, arg.typ)) + c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic}))) c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = @@ -1085,7 +1086,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; c.freeTemp(tmp) proc whichAsgnOpc(n: PNode): TOpcode = - case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind + let toSkip = abstractRange-{tyTypeDesc} + case n.typ.skipTypes(toSkip).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt of tyString, tyCString: @@ -1559,6 +1561,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = c.gABx(n, opcLdConst, dest, lit) of skType: genTypeLit(c, s.typ, dest) + of skGenericParam: + if c.prc.sym.kind == skMacro: + genRdVar(c, n, dest, flags) + else: + internalError(n.info, "cannot generate code for: " & s.name.s) else: internalError(n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: @@ -1690,6 +1697,14 @@ proc genParams(c: PCtx; params: PNode) = c.prc.slots[i] = (inUse: true, kind: slotFixedLet) c.prc.maxSlots = max(params.len, 1) +proc genGenericParams(c: PCtx; gp: PNode) = + var base = c.prc.maxSlots + for i in 0.. <gp.len: + var param = gp.sons[i].sym + param.position = base + i # XXX: fix this earlier; make it consistent with templates + c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet) + c.prc.maxSlots = base + gp.len + proc finalJumpTarget(c: PCtx; pc, diff: int) = internalAssert(-0x7fff < diff and diff < 0x7fff) let oldInstr = c.code[pc] @@ -1761,6 +1776,8 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) + if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty: + genGenericParams(c, s.ast[genericParamsPos]) if tfCapturesEnv in s.typ.flags: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 diff --git a/lib/system/gc.nim b/lib/system/gc.nim index e0db3fba4..58587cf7f 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -49,7 +49,7 @@ type waMarkGlobal, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, - waCollectWhite, + waCollectWhite #, waDebug TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's @@ -595,9 +595,15 @@ proc scan(s: PCell) = else: s.setColor(rcWhite) forAllChildren(s, waScan) - + proc collectWhite(s: PCell) = - if s.color == rcWhite and s notin gch.cycleRoots: + # This is a hacky way to deal with the following problem (bug #1796) + # Consider this content in cycleRoots: + # x -> a; y -> a where 'a' is an acyclic object so not included in + # cycleRoots itself. Then 'collectWhite' used to free 'a' twice. The + # 'isAllocatedPtr' check prevents this. This also means we do not need + # to query 's notin gch.cycleRoots' at all. + if isAllocatedPtr(gch.region, s) and s.color == rcWhite: s.setColor(rcBlack) forAllChildren(s, waCollectWhite) freeCyclicCell(gch, s) @@ -648,6 +654,28 @@ when useMarkForDebug or useBackupGc: if objStart != nil: markS(gch, objStart) +when logGC: + var + cycleCheckA: array[100, PCell] + cycleCheckALen = 0 + + proc alreadySeen(c: PCell): bool = + for i in 0 .. <cycleCheckALen: + if cycleCheckA[i] == c: return true + if cycleCheckALen == len(cycleCheckA): + gcAssert(false, "cycle detection overflow") + quit 1 + cycleCheckA[cycleCheckALen] = c + inc cycleCheckALen + + proc debugGraph(s: PCell) = + if alreadySeen(s): + writeCell("child cell (already seen) ", s) + else: + writeCell("cell {", s) + forAllChildren(s, waDebug) + c_fprintf(c_stdout, "}\n") + proc doOperation(p: pointer, op: TWalkOp) = if p == nil: return var c: PCell = usrToCell(p) @@ -690,6 +718,7 @@ proc doOperation(p: pointer, op: TWalkOp) = of waMarkPrecise: when useMarkForDebug or useBackupGc: add(gch.tempStack, c) + #of waDebug: debugGraph(c) proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, TWalkOp(op)) @@ -702,7 +731,6 @@ when useMarkForDebug or useBackupGc: proc collectRoots(gch: var TGcHeap) = for s in elements(gch.cycleRoots): - excl(gch.cycleRoots, s) collectWhite(s) proc collectCycles(gch: var TGcHeap) = diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim index 38ee1250a..1c39f43d5 100644 --- a/tests/gc/closureleak.nim +++ b/tests/gc/closureleak.nim @@ -7,7 +7,7 @@ from strutils import join type TFoo * = object id: int - func: proc(){.closure.} + fn: proc(){.closure.} var foo_counter = 0 var alive_foos = newseq[int](0) @@ -26,7 +26,7 @@ for i in 0 .. <10: for i in 0 .. <10: let f = newFoo() - f.func = proc = + f.fn = proc = echo f.id GC_fullcollect() diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim new file mode 100644 index 000000000..46fed6c45 --- /dev/null +++ b/tests/gc/cyclecollector.nim @@ -0,0 +1,21 @@ + +# Program to detect bug #1796 reliably + +type + Node = ref object + a, b: Node + leaf: string + +proc createCycle(leaf: string): Node = + new result + result.a = result + shallowCopy result.leaf, leaf + +proc main = + for i in 0 .. 100_000: + var leaf = "this is the leaf. it allocates" + let x = createCycle(leaf) + let y = createCycle(leaf) + echo "done ", getOccupiedMem() + +main() diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim index 27134d7dd..2213a83ac 100644 --- a/tests/gc/gctest.nim +++ b/tests/gc/gctest.nim @@ -196,7 +196,8 @@ write(stdout, "starting main...\n") main() GC_fullCollect() +# the M&S GC fails with this call and it's unclear why. Definitely something +# we need to fix! GC_fullCollect() writeln(stdout, GC_getStatistics()) write(stdout, "finished\n") - diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index e98a2871f..7fc5f479b 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -119,3 +119,24 @@ foo_2.intOrFloat foo_2.yinOrYang foo_3.yinOrYang +# bug 1859 + +type + TypeWith2Params[N, M: static[int]] = object + +proc bindBothParams[N](x: TypeWith2Params[N, N]) = discard +proc dontBind1[N,M](x: TypeWith2Params[N, M]) = discard +proc dontBind2(x: TypeWith2Params) = discard + +var bb_1: TypeWith2Params[2, 2] +var bb_2: TypeWith2Params[2, 3] + +bindBothParams(bb_1) +reject bindBothParams(bb_2) + +dontBind1 bb_1 +dontBind1 bb_2 + +dontBind2 bb_1 +dontBind2 bb_2 + diff --git a/tests/static/tstaticparammacro.nim b/tests/static/tstaticparammacro.nim index 7fb9e2014..ebd6caa47 100644 --- a/tests/static/tstaticparammacro.nim +++ b/tests/static/tstaticparammacro.nim @@ -10,6 +10,9 @@ AST a AST b (e: [55, 66], f: [77, 88]) 55 +10 +20Test +20 ''' """ @@ -50,3 +53,22 @@ macro mB(data: static[Tb]): stmt = mA(a) mB(b) +type + Foo[N: static[int], Z: static[string]] = object + +macro staticIntMacro(f: static[int]): stmt = echo f +staticIntMacro 10 + +var + x: Foo[20, "Test"] + +macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt = + echo N, Z + +genericMacro x + +template genericTemplate[N, Z](f: Foo[N, Z], ll = 3, zz = 12): int = N + +static: + echo genericTemplate(x) # Error: internal error: (filename: compiler/evaltempl.nim, line: 39) + diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index ae9905cde..7c7d71aa2 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -107,12 +107,15 @@ proc dllTests(r: var TResults, cat: Category, options: string) = # ------------------------------ GC tests ------------------------------------- proc gcTests(r: var TResults, cat: Category, options: string) = - template test(filename: expr): stmt = + template testWithoutMs(filename: expr): stmt = testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release -d:useRealtimeGC", cat, actionRun) + + template test(filename: expr): stmt = + testWithoutMs filename testSpec r, makeTest("tests/gc" / filename, options & " --gc:markAndSweep", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & @@ -124,13 +127,15 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gctest" test "gcleak3" test "gcleak4" - test "gcleak5" + # Disabled because it works and takes too long to run: + #test "gcleak5" test "weakrefs" test "cycleleak" test "closureleak" - test "refarrayleak" - test "stackrefleak" + testWithoutMs "refarrayleak" + test "stackrefleak" + test "cyclecollector" # ------------------------- threading tests ----------------------------------- diff --git a/web/question.txt b/web/question.txt index d3a2dd5c0..c27155858 100644 --- a/web/question.txt +++ b/web/question.txt @@ -110,6 +110,7 @@ General - Scite: Included - Gedit: The `Aporia .lang file <https://github.com/nimrod-code/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nimrod.lang>`_ - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit + - TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_) .. container:: standout |