diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 8 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 24 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 16 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 4 | ||||
-rw-r--r-- | compiler/commands.nim | 4 | ||||
-rw-r--r-- | compiler/destroyer.nim | 256 | ||||
-rw-r--r-- | compiler/dfa.nim | 8 | ||||
-rw-r--r-- | compiler/filter_tmpl.nim | 18 | ||||
-rw-r--r-- | compiler/filters.nim | 18 | ||||
-rw-r--r-- | compiler/lexer.nim | 30 | ||||
-rw-r--r-- | compiler/llstream.nim | 1 | ||||
-rw-r--r-- | compiler/lowerings.nim | 11 | ||||
-rw-r--r-- | compiler/msgs.nim | 1 | ||||
-rw-r--r-- | compiler/nimlexbase.nim | 2 | ||||
-rw-r--r-- | compiler/nversion.nim | 2 | ||||
-rw-r--r-- | compiler/options.nim | 1 | ||||
-rw-r--r-- | compiler/renderer.nim | 68 | ||||
-rw-r--r-- | compiler/rodread.nim | 7 | ||||
-rw-r--r-- | compiler/rodwrite.nim | 8 | ||||
-rw-r--r-- | compiler/semasgn.nim | 6 | ||||
-rw-r--r-- | compiler/semdestruct.nim | 59 | ||||
-rw-r--r-- | compiler/semexprs.nim | 18 | ||||
-rw-r--r-- | compiler/sempass2.nim | 28 | ||||
-rw-r--r-- | compiler/semstmts.nim | 61 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 16 | ||||
-rw-r--r-- | compiler/transf.nim | 21 |
26 files changed, 404 insertions, 292 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 97ff4b593..6519b698a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -871,7 +871,8 @@ type # mean that there is no destructor. # see instantiateDestructor in semdestruct.nim deepCopy*: PSym # overriden 'deepCopy' operation - assignment*: PSym # overriden '=' operator + assignment*: PSym # overriden '=' operation + sink*: PSym # overriden '=sink' operation methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown @@ -1047,6 +1048,8 @@ proc newNode*(kind: TNodeKind): PNode = proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode = result = newNode(kind) + if children.len > 0: + result.info = children[0].info result.sons = @children proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = @@ -1078,7 +1081,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.info = info result.options = gOptions result.owner = owner - result.offset = - 1 + result.offset = -1 result.id = getID() when debugIds: registerId(result) @@ -1290,6 +1293,7 @@ proc assignType*(dest, src: PType) = dest.align = src.align dest.destructor = src.destructor dest.deepCopy = src.deepCopy + dest.sink = src.sink dest.assignment = src.assignment dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 88944aea6..254248f9f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1248,17 +1248,31 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: - var L = int(lengthOrd(n.sons[1].typ)) - + let L = int(lengthOrd(n.sons[1].typ)) genNewSeqAux(p, d, intLiteral(L)) initLocExpr(p, n.sons[1], a) - for i in countup(0, L - 1): + # bug #5007; do not produce excessive C source code: + if L < 10: + for i in countup(0, L - 1): + initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) + elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) + elem.storage = OnHeap # we know that sequences are on the heap + initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) + arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) + genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + else: + var i: TLoc + getTemp(p, getSysType(tyInt), i) + let oldCode = p.s(cpsStmts) + linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, L.rope) initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) - elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) + elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), rdLoc(i)) elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) - arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) + arr.r = rfmt(nil, "$1[$2]", rdLoc(a), rdLoc(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + lineF(p, cpsStmts, "}$n", []) + proc genNewFinalize(p: BProc, e: PNode) = var diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 74779f598..5434d87b2 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -141,9 +141,13 @@ template preserveBreakIdx(body: untyped): untyped = p.breakIdx = oldBreakIdx proc genState(p: BProc, n: PNode) = - internalAssert n.len == 1 and n.sons[0].kind == nkIntLit - let idx = n.sons[0].intVal - linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope) + internalAssert n.len == 1 + let n0 = n[0] + if n0.kind == nkIntLit: + let idx = n.sons[0].intVal + linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope) + elif n0.kind == nkStrLit: + linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope) proc genGotoState(p: BProc, n: PNode) = # we resist the temptation to translate it into duff's device as it later @@ -157,10 +161,12 @@ proc genGotoState(p: BProc, n: PNode) = p.beforeRetNeeded = true lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", []) var statesCounter = lastOrd(n.sons[0].typ) - if n.len == 2 and n[1].kind == nkIntLit: + if n.len >= 2 and n[1].kind == nkIntLit: statesCounter = n[1].intVal + let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope + else: rope"STATE" for i in 0 .. statesCounter: - lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)]) + lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)]) lineF(p, cpsStmts, "}$n", []) proc genBreakState(p: BProc, n: PNode) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c9705e784..4c85294b2 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -278,7 +278,7 @@ proc ccgIntroducedPtr(s: PSym): bool = elif tfByCopy in pt.flags: return false case pt.kind of tyObject: - if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2): + if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt.sons[0] == nil): result = false # no need, because no subtyping possible @@ -286,7 +286,7 @@ proc ccgIntroducedPtr(s: PSym): bool = result = true # ordinary objects are always passed by reference, # otherwise casting doesn't work of tyTuple: - result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options) + result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options) else: result = false proc fillResult(param: PNode) = diff --git a/compiler/commands.nim b/compiler/commands.nim index ce50b1a79..bae1fda38 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -666,6 +666,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(switch, arg, pass, info) if config != nil: config.cppDefine(arg) + of "newruntime": + expectNoArg(switch, arg, pass, info) + newDestructors = true + defineSymbol("nimNewRuntime") else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index e7ff00bb9..afa2e5e50 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -76,7 +76,10 @@ ## inefficiencies. A better strategy is to collect all the temporaries ## in a single object that we put into a single try-finally that ## surrounds the proc body. This means the code stays quite efficient -## when compiled to C. +## when compiled to C. In fact, we do the same for variables, so +## destructors are called when the proc returns, not at scope exit! +## This makes certains idioms easier to support. (Taking the slice +## of a temporary object.) ## ## foo(bar(X(), Y())) ## X and Y get destroyed after bar completes: @@ -94,103 +97,17 @@ import template hasDestructor(t: PType): bool = tfHasAsgn in t.flags -when false: - type - VarInfo = object - hasInitValue: bool - addrTaken: bool - assigned: int # we don't care about the 'var' vs 'let' - # distinction; it's an optimization pass - read: int - scope: int # the scope the variable is declared in - - Con = object - t: Table[int, VarInfo] - owner: PSym - scope: int - - const - InterestingSyms = {skVar, skResult} - - proc collectData(c: var Con; n: PNode) - - proc collectDef(c: var Con; n: PNode; hasInitValue: bool) = - if n.kind == nkSym: - c.t[n.sym.id] = VarInfo(hasInitValue: hasInitValue, - addrTaken: false, assigned: 0, read: 0, - scope: scope) - - proc collectVarSection(c: var Con; n: PNode) = - for a in n: - if a.kind == nkCommentStmt: continue - if a.kind == nkVarTuple: - collectData(c, a.lastSon) - for i in 0 .. a.len-3: collectDef(c, a[i], a.lastSon != nil) - else: - collectData(c, a.lastSon) - if a.lastSon.kind != nkEmpty: - collectDef(c, a.sons[0], a.lastSon != nil) - - proc collectData(c: var Con; n: PNode) = - case n.kind - of nkAsgn, nkFastAsgn: - if n[0].kind == nkSym and (let s = n[0].sym; s.owner == c.owner and - s.kind in InterestingSyms): - inc c.t[s.id].assigned - collectData(c, n[1]) - of nkSym: - if (let s = n[0].sym; s.owner == c.owner and - s.kind in InterestingSyms): - inc c.t[s.id].read - of nkAddr, nkHiddenAddr: - var n = n[0] - while n.kind == nkBracketExpr: n = n[0] - if (let s = n[0].sym; s.owner == c.owner and - s.kind in InterestingSyms): - c.t[s.id].addrTaken = true - - of nkCallKinds: - if n.sons[0].kind == nkSym: - let s = n.sons[0].sym - if s.magic != mNone: - genMagic(c, n, s.magic) - else: - genCall(c, n) - else: - genCall(c, n) - of nkCharLit..nkNilLit, nkIdent: discard - of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr, - nkDerefExpr, nkHiddenDeref: - collectData(c, n[0]) - of nkIfStmt, nkIfExpr: genIf(c, n) - of nkWhenStmt: - # This is "when nimvm" node. Chose the first branch. - collectData(c, n.sons[0].sons[1]) - of nkCaseStmt: genCase(c, n) - of nkWhileStmt: genWhile(c, n) - of nkBlockExpr, nkBlockStmt: genBlock(c, n) - of nkReturnStmt: genReturn(c, n) - of nkRaiseStmt: genRaise(c, n) - of nkBreakStmt: genBreak(c, n) - of nkTryStmt: genTry(c, n) - of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, - nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr: - for x in n: collectData(c, x) - of nkPragmaBlock: collectData(c, n.lastSon) - of nkDiscardStmt: collectData(c, n.sons[0]) - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr, - nkCast: - collectData(c, n.sons[1]) - of nkObjDownConv, nkStringToCString, nkCStringToString: - collectData(c, n.sons[0]) - of nkVarSection, nkLetSection: collectVarSection(c, n) - else: discard +const + InterestingSyms = {skVar, skResult, skLet} type Con = object owner: PSym g: ControlFlowGraph - tmps: PType + jumpTargets: IntSet + tmpObj: PType + tmp: PSym + destroys, topLevelVars: PNode proc isHarmlessVar*(s: PSym; c: Con): bool = # 's' is harmless if it used only once and its @@ -224,29 +141,156 @@ proc isHarmlessVar*(s: PSym; c: Con): bool = # L3 # # So this analysis is for now overly conservative, but correct. - discard + var defsite = -1 + var usages = 0 + for i in 0..<c.g.len: + case c.g[i].kind + of def: + if c.g[i].sym == s: + if defsite < 0: defsite = i + else: return false + of use: + if c.g[i].sym == s: + if defsite < 0: return false + for j in defsite .. i: + # not within the same basic block? + if j in c.jumpTargets: return false + # if we want to die after the first 'use': + if usages > 1: return false + inc usages + of useWithinCall: + if c.g[i].sym == s: return false + of goto, fork: + discard "we do not perform an abstract interpretation yet" template interestingSym(s: PSym): bool = - s.owner == owner and s.kind in InterestingSyms and hasDestructor(s.typ) + s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) + +proc genSink(t: PType; dest: PNode): PNode = + let op = if t.sink != nil: t.sink else: t.assignment + assert op != nil + result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest)) + +proc genCopy(t: PType; dest: PNode): PNode = + assert t.assignment != nil + result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest)) + +proc genDestroy(t: PType; dest: PNode): PNode = + assert t.destructor != nil + result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest)) -proc p(n, parent: PNode; c: var Con) = +proc addTopVar(c: var Con; v: PNode) = + c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) + +proc p(n: PNode; c: var Con): PNode + +template recurse(n, dest) = + for i in 0..<n.len: + dest.add p(n[i], c) + +proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = + if ri.kind in nkCallKinds: + result = genSink(ri.typ, dest) + # watch out and no not transform 'ri' twice if it's a call: + let ri2 = copyNode(ri) + recurse(ri, ri2) + result.add ri2 + elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): + result = genSink(ri.typ, dest) + result.add p(ri, c) + else: + result = genCopy(ri.typ, dest) + result.add p(ri, c) + +proc p(n: PNode; c: var Con): PNode = case n.kind of nkVarSection, nkLetSection: discard "transform; var x = y to var x; x op y where op is a move or copy" + result = newNodeI(nkStmtList, n.info) + + for i in 0..<n.len: + let it = n[i] + let L = it.len-1 + let ri = it[L] + if it.kind == nkVarTuple and hasDestructor(ri.typ): + let x = lowerTupleUnpacking(it, c.owner) + result.add p(x, c) + elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): + for j in 0..L-2: + let v = it[j] + doAssert v.kind == nkSym + # move the variable declaration to the top of the frame: + c.addTopVar v + # make sure it's destroyed at the end of the proc: + c.destroys.add genDestroy(v.typ, v) + if ri.kind != nkEmpty: + let r = moveOrCopy(v, ri, c) + result.add r + else: + # keep it, but transform 'ri': + var varSection = copyNode(n) + var itCopy = copyNode(it) + for j in 0..L-1: + itCopy.add it[j] + itCopy.add p(ri, c) + varSection.add itCopy + result.add varSection of nkCallKinds: if n.typ != nil and hasDestructor(n.typ): discard "produce temp creation" + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info) + f.typ = n.typ + rawAddField c.tmpObj, f + var m = genSink(n.typ, rawDirectAccess(c.tmp, f)) + var call = copyNode(n) + recurse(n, call) + m.add call + result.add m + result.add rawDirectAccess(c.tmp, f) + c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f)) + else: + result = copyNode(n) + recurse(n, result) of nkAsgn, nkFastAsgn: if n[0].kind == nkSym and interestingSym(n[0].sym): - discard "use move or assignment" + result = moveOrCopy(n[0], n[1], c) + else: + result = copyNode(n) + recurse(n, result) + of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, + nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: + result = n else: - for i in 0..<n.len: - p(n[i], n, c) - -proc injectDestructorCalls*(owner: PSym; n: PNode; - disableExceptions = false): PNode = - when false: - var c = Con(t: initTable[int, VarInfo](), owner: owner) - collectData(c, n) - var allTemps = createObj(owner, n.info) + result = copyNode(n) + recurse(n, result) + +proc injectDestructorCalls*(owner: PSym; n: PNode): PNode = + var c: Con + c.owner = owner + c.tmp = newSym(skTemp, getIdent":d", owner, n.info) + c.tmpObj = createObj(owner, n.info) + c.tmp.typ = c.tmpObj + c.destroys = newNodeI(nkStmtList, n.info) + c.topLevelVars = newNodeI(nkVarSection, n.info) let cfg = constructCfg(owner, n) + shallowCopy(c.g, cfg) + c.jumpTargets = initIntSet() + for i in 0..<c.g.len: + if c.g[i].kind in {goto, fork}: + c.jumpTargets.incl(i+c.g[i].dest) + let body = p(n, c) + if c.tmp.typ.n.len > 0: + c.addTopVar(newSymNode c.tmp) + result = newNodeI(nkStmtList, n.info) + if c.topLevelVars.len > 0: + result.add c.topLevelVars + if c.destroys.len > 0: + result.add newTryFinally(body, c.destroys) + else: + result.add body + + when defined(nimDebugDestroys): + echo "------------------------------------" + echo owner.name.s, " transformed to: " + echo result diff --git a/compiler/dfa.nim b/compiler/dfa.nim index dd9dd4c79..ca1849a3c 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -235,14 +235,14 @@ proc genTry(c: var Con; n: PNode) = proc genRaise(c: var Con; n: PNode) = gen(c, n.sons[0]) - c.code.add Instr(n: n, kind: goto, dest: high(int)) + c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) proc genReturn(c: var Con; n: PNode) = if n.sons[0].kind != nkEmpty: gen(c, n.sons[0]) - c.code.add Instr(n: n, kind: goto, dest: high(int)) + c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) const - InterestingSyms = {skVar, skResult} + InterestingSyms = {skVar, skResult, skLet} proc genUse(c: var Con; n: PNode) = var n = n @@ -279,7 +279,7 @@ proc genMagic(c: var Con; n: PNode; m: TMagic) = for i in 2..<n.len: gen(c, n[i]) of mExit: genCall(c, n) - c.code.add Instr(n: n, kind: goto, dest: high(int)) + c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) else: genCall(c, n) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 361b3d276..ca9a3a801 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -13,14 +13,10 @@ import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, renderer, filters -proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream - # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2") -# implementation - type TParseState = enum psDirective, psTempl - TTmplParser{.final.} = object + TTmplParser = object inp: PLLStream state: TParseState info: TLineInfo @@ -61,6 +57,10 @@ proc scanPar(p: var TTmplParser, d: int) = proc withInExpr(p: TTmplParser): bool {.inline.} = result = p.par > 0 or p.bracket > 0 or p.curly > 0 +const + LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^', + '|', '%', '&', '$', '@', '~', ','} + proc parseLine(p: var TTmplParser) = var j = 0 while p.x[j] == ' ': inc(j) @@ -77,7 +77,7 @@ proc parseLine(p: var TTmplParser) = inc(j) scanPar(p, j) - p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x) + p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs) case keyw of "end": if p.indent >= 2: @@ -88,14 +88,14 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, "#end") of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator", - "converter", "macro", "template", "method": + "converter", "macro", "template", "method", "func": llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) inc(p.indent, 2) of "elif", "of", "else", "except", "finally": llStreamWrite(p.outp, spaces(p.indent - 2)) llStreamWrite(p.outp, substr(p.x, d)) - of "wLet", "wVar", "wConst", "wType": + of "let", "var", "const", "type": llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) if not p.x.contains({':', '='}): @@ -199,7 +199,7 @@ proc parseLine(p: var TTmplParser) = inc(j) llStreamWrite(p.outp, "\\n\"") -proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream = var p: TTmplParser p.info = newLineInfo(filename, 0, 0) p.outp = llStreamOpen("") diff --git a/compiler/filters.nim b/compiler/filters.nim index d1a6409ff..37df628ed 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -13,14 +13,6 @@ import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, renderer -proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream -proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream - # helpers to retrieve arguments: -proc charArg*(n: PNode, name: string, pos: int, default: char): char -proc strArg*(n: PNode, name: string, pos: int, default: string): string -proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool -# implementation - proc invalidPragma(n: PNode) = localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) @@ -35,26 +27,26 @@ proc getArg(n: PNode, name: string, pos: int): PNode = elif i == pos: return n.sons[i] -proc charArg(n: PNode, name: string, pos: int, default: char): char = +proc charArg*(n: PNode, name: string, pos: int, default: char): char = var x = getArg(n, name, pos) if x == nil: result = default elif x.kind == nkCharLit: result = chr(int(x.intVal)) else: invalidPragma(n) -proc strArg(n: PNode, name: string, pos: int, default: string): string = +proc strArg*(n: PNode, name: string, pos: int, default: string): string = var x = getArg(n, name, pos) if x == nil: result = default elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal else: invalidPragma(n) -proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = +proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool = var x = getArg(n, name, pos) if x == nil: result = default elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false else: invalidPragma(n) -proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream = var pattern = strArg(call, "startswith", 1, "") var leading = boolArg(call, "leading", 2, true) var trailing = boolArg(call, "trailing", 3, true) @@ -68,7 +60,7 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = llStreamWriteln(result, line) llStreamClose(stdin) -proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream = var sub = strArg(call, "sub", 1, "") if len(sub) == 0: invalidPragma(call) var by = strArg(call, "by", 2, "") diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 68b0164d4..895848e77 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -129,6 +129,7 @@ type when defined(nimpretty): offsetA*, offsetB*: int # used for pretty printing so that literals # like 0b01 or r"\L" are unaffected + commentOffsetA*, commentOffsetB*: int TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer @@ -144,6 +145,10 @@ type when defined(nimsuggest): previousToken: TLineInfo +when defined(nimpretty): + var + gIndentationWidth*: int + var gLinesCompiled*: int # all lines that have been compiled proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = @@ -151,6 +156,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = when defined(nimpretty): result.offsetA = tok.offsetA result.offsetB = tok.offsetB + result.commentOffsetA = tok.commentOffsetA + result.commentOffsetB = tok.commentOffsetB proc isKeyword*(kind: TTokType): bool = result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) @@ -198,6 +205,9 @@ proc initToken*(L: var TToken) = L.fNumber = 0.0 L.base = base10 L.ident = nil + when defined(nimpretty): + L.commentOffsetA = 0 + L.commentOffsetB = 0 proc fillToken(L: var TToken) = L.tokType = tkInvalid @@ -208,6 +218,9 @@ proc fillToken(L: var TToken) = L.fNumber = 0.0 L.base = base10 L.ident = nil + when defined(nimpretty): + L.commentOffsetA = 0 + L.commentOffsetB = 0 proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; cache: IdentCache) = @@ -996,18 +1009,27 @@ proc skip(L: var TLexer, tok: var TToken) = of '#': # do not skip documentation comment: if buf[pos+1] == '#': break + when defined(nimpretty): + tok.commentOffsetA = L.offsetBase + pos if buf[pos+1] == '[': skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf + when defined(nimpretty): + tok.commentOffsetB = L.offsetBase + pos else: tokenBegin(tok, pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) tokenEndIgnore(tok, pos+1) + when defined(nimpretty): + tok.commentOffsetB = L.offsetBase + pos + 1 else: break # EndOfFile also leaves the loop tokenEndPrevious(tok, pos-1) L.bufpos = pos + when defined(nimpretty): + if gIndentationWidth <= 0: + gIndentationWidth = tok.indent proc rawGetTok*(L: var TLexer, tok: var TToken) = template atTokenEnd() {.dirty.} = @@ -1030,7 +1052,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) - if c in SymStartChars - {'r', 'R', 'l'}: + if c in SymStartChars - {'r', 'R'}: getSymbol(L, tok) else: case c @@ -1047,12 +1069,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = of ',': tok.tokType = tkComma inc(L.bufpos) - of 'l': - # if we parsed exactly one character and its a small L (l), this - # is treated as a warning because it may be confused with the number 1 - if L.buf[L.bufpos+1] notin (SymChars + {'_'}): - lexMessage(L, warnSmallLshouldNotBeUsed) - getSymbol(L, tok) of 'r', 'R': if L.buf[L.bufpos + 1] == '\"': inc(L.bufpos) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 0a1e09fc8..42bbb7600 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -84,7 +84,6 @@ const AdditionalLineContinuationOprs = {'#', ':', '='} proc endsWithOpr*(x: string): bool = - # also used by the standard template filter: result = x.endsWith(LineContinuationOprs) proc continueLine(line: string, inTripleString: bool): bool {.inline.} = diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 033472c07..f895e887c 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -70,6 +70,9 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode = lit.intVal = i addSon(result, lit) +proc newTryFinally*(body, final: PNode): PNode = + result = newTree(nkTryStmt, body, newTree(nkFinally, final)) + proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) @@ -139,6 +142,14 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode = addSon(result, newSymNode(field)) result.typ = field.typ +proc rawDirectAccess*(obj, field: PSym): PNode = + # returns a.field as a node + assert field.kind == skField + result = newNodeI(nkDotExpr, field.info) + addSon(result, newSymNode obj) + addSon(result, newSymNode field) + result.typ = field.typ + proc lookupInRecord(n: PNode, id: int): PSym = result = nil case n.kind diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c988141e5..8d43103db 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -500,6 +500,7 @@ type fileIndex*: int32 when defined(nimpretty): offsetA*, offsetB*: int + commentOffsetA*, commentOffsetB*: int TErrorOutput* = enum eStdOut diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index c395a3709..2e7416645 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -123,7 +123,7 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int = result = pos + 1 # nothing to do else: fillBuffer(L) - L.offsetBase += pos + L.offsetBase += pos + 1 L.bufpos = 0 result = 0 L.lineStart = result diff --git a/compiler/nversion.nim b/compiler/nversion.nim index 4d4fe6c95..85265a7c0 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -13,5 +13,5 @@ const MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? VersionAsString* = system.NimVersion - RodFileVersion* = "1222" # modify this if the rod-format changes! + RodFileVersion* = "1223" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 4888cc6ba..86e6006d7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -144,6 +144,7 @@ var isServing*: bool = false gNoNimblePath* = false gExperimentalMode*: bool + newDestructors*: bool proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 4fbac45ab..f3b4527df 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -37,6 +37,7 @@ type inGenericParams: bool checkAnon: bool # we're in a context that can contain sfAnon inPragma: int + pendingNewlineCount: int when defined(nimpretty): origContent: string @@ -62,9 +63,31 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string = else: result = '`' & x & '`' +when not defined(nimpretty): + const + IndentWidth = 2 + longIndentWid = IndentWidth * 2 +else: + template IndentWidth: untyped = lexer.gIndentationWidth + template longIndentWid: untyped = IndentWidth() * 2 + +proc minmaxLine(n: PNode): (int, int) = + case n.kind + of nkTripleStrLit: + result = (n.info.line.int, n.info.line.int + countLines(n.strVal)) + of nkCommentStmt: + result = (n.info.line.int, n.info.line.int + countLines(n.comment)) + else: + result = (n.info.line.int, n.info.line.int) + for i in 0 ..< safeLen(n): + let (currMin, currMax) = minmaxLine(n[i]) + if currMin < result[0]: result[0] = currMin + if currMax > result[1]: result[1] = currMax + +proc lineDiff(a, b: PNode): int = + result = minmaxLine(b)[0] - minmaxLine(a)[1] + const - IndentWidth = 2 - longIndentWid = 4 MaxLineLen = 80 LineCommentColumn = 30 @@ -90,7 +113,8 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) = proc addPendingNL(g: var TSrcGen) = if g.pendingNL >= 0: - addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) + let newlines = repeat("\n", clamp(g.pendingNewlineCount, 1, 3)) + addTok(g, tkSpaces, newlines & spaces(g.pendingNL)) g.lineLen = g.pendingNL g.pendingNL = - 1 g.pendingWhitespace = -1 @@ -114,11 +138,17 @@ proc putNL(g: var TSrcGen) = proc optNL(g: var TSrcGen, indent: int) = g.pendingNL = indent - g.lineLen = indent # BUGFIX + g.lineLen = indent + g.pendingNewlineCount = 0 proc optNL(g: var TSrcGen) = optNL(g, g.indent) +proc optNL(g: var TSrcGen; a, b: PNode) = + g.pendingNL = g.indent + g.lineLen = g.indent + g.pendingNewlineCount = lineDiff(a, b) + proc indentNL(g: var TSrcGen) = inc(g.indent, IndentWidth) g.pendingNL = g.indent @@ -306,10 +336,14 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc atom(g: TSrcGen; n: PNode): string = when defined(nimpretty): + let comment = if n.info.commentOffsetA < n.info.commentOffsetB: + " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB) + else: + "" if n.info.offsetA <= n.info.offsetB: # for some constructed tokens this can not be the case and we're better # off to not mess with the offset then. - return substr(g.origContent, n.info.offsetA, n.info.offsetB) + return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment var f: float32 case n.kind of nkEmpty: result = "" @@ -577,12 +611,16 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = if n.kind == nkEmpty: return if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: if doIndent: indentNL(g) - for i in countup(0, sonsLen(n) - 1): - optNL(g) - if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: - gstmts(g, n.sons[i], c, doIndent=false) + let L = n.len + for i in 0 .. L-1: + if i > 0: + optNL(g, n[i-1], n[i]) else: - gsub(g, n.sons[i]) + optNL(g) + if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: + gstmts(g, n[i], c, doIndent=false) + else: + gsub(g, n[i]) gcoms(g) if doIndent: dedent(g) else: @@ -1384,7 +1422,7 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree -proc renderModule*(n: PNode, filename: string, +proc renderModule*(n: PNode, infile, outfile: string, renderFlags: TRenderFlags = {}) = var f: File @@ -1392,9 +1430,9 @@ proc renderModule*(n: PNode, filename: string, initSrcGen(g, renderFlags) when defined(nimpretty): try: - g.origContent = readFile(filename) + g.origContent = readFile(infile) except IOError: - rawMessage(errCannotOpenFile, filename) + rawMessage(errCannotOpenFile, infile) for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) @@ -1406,11 +1444,11 @@ proc renderModule*(n: PNode, filename: string, gcoms(g) if optStdout in gGlobalOptions: write(stdout, g.buf) - elif open(f, filename, fmWrite): + elif open(f, outfile, fmWrite): write(f, g.buf) close(f) else: - rawMessage(errCannotOpenFile, filename) + rawMessage(errCannotOpenFile, outfile) proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = initSrcGen(r, renderFlags) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 31b54d760..2546aa77a 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -336,10 +336,13 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = if r.s[r.pos] == '\17': inc(r.pos) result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info) - while r.s[r.pos] == '\18': + if r.s[r.pos] == '\18': + inc(r.pos) + result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info) + while r.s[r.pos] == '\19': inc(r.pos) let x = decodeVInt(r.s, r.pos) - doAssert r.s[r.pos] == '\19' + doAssert r.s[r.pos] == '\20' inc(r.pos) let y = rrGetSym(r, decodeVInt(r.s, r.pos), info) result.methods.safeAdd((x, y)) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index fb50c6473..1bc136acf 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -245,10 +245,14 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = add(result, '\17') encodeVInt(t.assignment.id, result) pushSym(w, t.assignment) - for i, s in items(t.methods): + if t.sink != nil: add(result, '\18') - encodeVInt(i, result) + encodeVInt(t.sink.id, result) + pushSym(w, t.sink) + for i, s in items(t.methods): add(result, '\19') + encodeVInt(i, result) + add(result, '\20') encodeVInt(s.id, result) pushSym(w, s) encodeLoc(w, t.loc, result) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index dbb2a140b..caed11341 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -92,13 +92,13 @@ proc newAsgnStmt(le, ri: PNode): PNode = result.sons[0] = le result.sons[1] = ri -proc newDestructorCall(op: PSym; x: PNode): PNode = +proc newOpCall(op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ.sons[0]) result.add(newSymNode(op)) result.add x proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = - result = newAsgnStmt(x, newDestructorCall(op, y)) + result = newAsgnStmt(x, newOpCall(op, y)) proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = case c.kind @@ -107,7 +107,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = if op != nil: markUsed(c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) - body.add newDestructorCall(op, x) + body.add newOpCall(op, x) result = true of attachedAsgn: if tfHasAsgn in t.flags: diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index b09404b39..51109ec37 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -184,62 +184,3 @@ proc createDestructorCall(c: PContext, s: PSym): PNode = useSym(destructableT.destructor, c.graph.usageSym), useSym(s, c.graph.usageSym)])) result = newNode(nkDefer, s.info, @[call]) - -proc insertDestructors(c: PContext, - varSection: PNode): tuple[outer, inner: PNode] = - # Accepts a var or let section. - # - # When a var section has variables with destructors - # the var section is split up and finally blocks are inserted - # immediately after all "destructable" vars - # - # In case there were no destrucable variables, the proc returns - # (nil, nil) and the enclosing stmt-list requires no modifications. - # - # Otherwise, after the try blocks are created, the rest of the enclosing - # stmt-list should be inserted in the most `inner` such block (corresponding - # to the last variable). - # - # `outer` is a statement list that should replace the original var section. - # It will include the new truncated var section followed by the outermost - # try block. - let totalVars = varSection.sonsLen - for j in countup(0, totalVars - 1): - let - varId = varSection[j][0] - varTyp = varId.sym.typ - info = varId.info - - if varTyp == nil or sfGlobal in varId.sym.flags: continue - let destructableT = instantiateDestructor(c, varTyp) - - if destructableT != nil: - var tryStmt = newNodeI(nkTryStmt, info) - - if j < totalVars - 1: - var remainingVars = newNodeI(varSection.kind, info) - remainingVars.sons = varSection.sons[(j+1)..varSection.len-1] - let (outer, inner) = insertDestructors(c, remainingVars) - if outer != nil: - tryStmt.addSon(outer) - result.inner = inner - else: - result.inner = newNodeI(nkStmtList, info) - result.inner.addSon(remainingVars) - tryStmt.addSon(result.inner) - else: - result.inner = newNodeI(nkStmtList, info) - tryStmt.addSon(result.inner) - - tryStmt.addSon( - newNode(nkFinally, info, @[ - semStmt(c, newNode(nkCall, info, @[ - useSym(destructableT.destructor, c.graph.usageSym), - useSym(varId.sym, c.graph.usageSym)]))])) - - result.outer = newNodeI(nkStmtList, info) - varSection.sons.setLen(j+1) - result.outer.addSon(varSection) - result.outer.addSon(tryStmt) - - return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c336afc89..180754168 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1296,10 +1296,13 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le.sons[0] - if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: - n.sons[0] = x # 'result[]' --> 'result' - n.sons[1] = takeImplicitAddr(c, ri) - x.typ.flags.incl tfVarIsPtr + if x.typ.kind == tyVar and x.kind == nkSym: + if x.sym.kind == skResult: + n.sons[0] = x # 'result[]' --> 'result' + n.sons[1] = takeImplicitAddr(c, ri) + if x.sym.kind != skParam: + # XXX This is hacky. See bug #4910. + x.typ.flags.incl tfVarIsPtr #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info template resultTypeIsInferrable(typ: PType): untyped = @@ -1383,9 +1386,10 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = typeMismatch(n.info, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs, n.info) - if tfHasAsgn in lhs.typ.flags and not lhsIsResult and - mode != noOverloadedAsgn: - return overloadedAsgn(c, lhs, n.sons[1]) + if not newDestructors: + if tfHasAsgn in lhs.typ.flags and not lhsIsResult and + mode != noOverloadedAsgn: + return overloadedAsgn(c, lhs, n.sons[1]) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 17bdf0902..17d9c9840 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -19,18 +19,6 @@ when defined(useDfa): # # * effect+exception tracking # * "usage before definition" checking -# * checks for invalid usages of compiletime magics (not implemented) -# * checks for invalid usages of NimNode (not implemented) -# * later: will do an escape analysis for closures at least - -# Predefined effects: -# io, time (time dependent), gc (performs GC'ed allocation), exceptions, -# side effect (accesses global), store (stores into *type*), -# store_unknown (performs some store) --> store(any)|store(x) -# load (loads from *type*), recursive (recursive call), unsafe, -# endless (has endless loops), --> user effects are defined over *patterns* -# --> a TR macro can annotate the proc with user defined annotations -# --> the effect system can access these # ------------------------ exception and tag tracking ------------------------- @@ -251,6 +239,7 @@ proc useVar(a: PEffects, n: PNode) = (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) markGcUnsafe(a, s) + markSideEffect(a, s) else: markSideEffect(a, s) @@ -593,6 +582,12 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = if paramType != nil and paramType.kind == tyVar: if n.kind == nkSym and isLocalVar(tracked, n.sym): makeVolatile(tracked, n.sym) + if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags: + let argtype = skipTypes(a.typ, abstractInst) + # XXX figure out why this can be a non tyProc here. See httpclient.nim for an + # example that triggers it. + if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe: + localError(n.info, $n & " is not GC safe") notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = @@ -745,7 +740,7 @@ proc track(tracked: PEffects, n: PNode) = if not (a.kind == nkSym and a.sym == tracked.owner): markSideEffect(tracked, a) if a.kind != nkSym or a.sym.magic != mNBindSym: - for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) + for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: let arg = n.sons[1] @@ -982,9 +977,10 @@ proc trackProc*(s: PSym, body: PNode) = message(s.info, warnLockLevel, "declared lock level is $1, but real lock level is $2" % [$s.typ.lockLevel, $t.maxLockLevel]) - if s.kind == skFunc: - when defined(dfa): dataflowAnalysis(s, body) - trackWrites(s, body) + when false: + if s.kind == skFunc: + when defined(dfa): dataflowAnalysis(s, body) + trackWrites(s, body) proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a83de6d27..c6e03cef3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -100,15 +100,16 @@ proc semProc(c: PContext, n: PNode): PNode include semdestruct proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = - if efAllowDestructor notin flags and - n.kind in nkCallKinds+{nkObjConstr,nkBracket}: - if instantiateDestructor(c, n.typ) != nil: - localError(n.info, warnDestructor) - # This still breaks too many things: - when false: - if efDetermineType notin flags and n.typ.kind == tyTypeDesc and - c.p.owner.kind notin {skTemplate, skMacro}: - localError(n.info, errGenerated, "value expected, but got a type") + if not newDestructors: + if efAllowDestructor notin flags and + n.kind in nkCallKinds+{nkObjConstr,nkBracket}: + if instantiateDestructor(c, n.typ) != nil: + localError(n.info, warnDestructor) + # This still breaks too many things: + when false: + if efDetermineType notin flags and n.typ.kind == tyTypeDesc and + c.p.owner.kind notin {skTemplate, skMacro}: + localError(n.info, errGenerated, "value expected, but got a type") proc semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) @@ -399,7 +400,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = # in order for this transformation to be correct. let L = identDefs.len let value = identDefs[L-1] - if value.typ != nil and tfHasAsgn in value.typ.flags: + if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors: # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()': identDefs.sons[L-1] = emptyNode if result.kind != nkStmtList: @@ -607,7 +608,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkPar: v.ast = def[j] setVarType(v, tup.sons[j]) b.sons[j] = newSymNode(v) - addDefer(c, result, v) + if not newDestructors: addDefer(c, result, v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) @@ -1276,9 +1277,30 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize of "destroy", "=destroy": - doDestructorStuff(c, s, n) - if not experimentalMode(c): - localError n.info, "use the {.experimental.} pragma to enable destructors" + if newDestructors: + let t = s.typ + var noError = false + if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar: + var obj = t.sons[1].sons[0] + while true: + incl(obj.flags, tfHasAsgn) + if obj.kind == tyGenericBody: obj = obj.lastSon + elif obj.kind == tyGenericInvocation: obj = obj.sons[0] + else: break + if obj.kind in {tyObject, tyDistinct}: + if obj.destructor.isNil: + obj.destructor = s + else: + localError(n.info, errGenerated, + "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + noError = true + if not noError: + localError(n.info, errGenerated, + "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") + else: + doDestructorStuff(c, s, n) + if not experimentalMode(c): + localError n.info, "use the {.experimental.} pragma to enable destructors" incl(s.flags, sfUsed) of "deepcopy", "=deepcopy": if s.typ.len == 2 and @@ -1303,7 +1325,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) - of "=": + of "=", "=sink": if s.magic == mAsgn: return incl(s.flags, sfUsed) let t = s.typ @@ -1321,14 +1343,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = objB = objB.sons[0] else: break if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): - if obj.assignment.isNil: - obj.assignment = s + let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) + if opr[].isNil: + opr[] = s else: localError(n.info, errGenerated, - "cannot bind another '=' to: " & typeToString(obj)) + "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) return localError(n.info, errGenerated, - "signature for '=' must be proc[T: object](x: var T; y: T)") + "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") else: if sfOverriden in s.flags: localError(n.info, errGenerated, diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a3953d87e..172a557b3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -357,11 +357,17 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = assert newbody.kind in {tyRef, tyPtr} assert newbody.lastSon.typeInst == nil newbody.lastSon.typeInst = result - let asgn = newbody.assignment - if asgn != nil and sfFromGeneric notin asgn.flags: - # '=' needs to be instantiated for generics when the type is constructed: - newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, - attachedAsgn, 1) + template typeBound(field) = + let opr = newbody.field + if opr != nil and sfFromGeneric notin opr.flags: + # '=' needs to be instantiated for generics when the type is constructed: + newbody.field = cl.c.instTypeBoundOp(cl.c, opr, result, cl.info, + attachedAsgn, 1) + # we need to produce the destructor first here because generated '=' + # and '=sink' operators can rely on it: + if newDestructors: typeBound(destructor) + typeBound(assignment) + typeBound(sink) let methods = skipTypes(bbody, abstractPtrs).methods for col, meth in items(methods): # we instantiate the known methods belonging to that type, this causes diff --git a/compiler/transf.nim b/compiler/transf.nim index f1ee49a54..c3d12dafe 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -21,9 +21,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, - lambdalifting, sempass2, lowerings, lookups - -# implementation + lambdalifting, sempass2, lowerings, lookups, destroyer type PTransNode* = distinct PNode @@ -45,7 +43,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' - deferDetected, tooEarly: bool + deferDetected, tooEarly, needsDestroyPass: bool PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -782,7 +780,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = nkBlockStmt, nkBlockExpr}: oldDeferAnchor = c.deferAnchor c.deferAnchor = n - + if n.typ != nil and tfHasAsgn in n.typ.flags: + c.needsDestroyPass = true case n.kind of nkSym: result = transformSym(c, n) @@ -972,9 +971,11 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = result = processTransf(c, result, prc) liftDefer(c, result) #result = liftLambdas(prc, result) - incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) - #if prc.name.s == "testbody": + if c.needsDestroyPass and newDestructors: + result = injectDestructorCalls(prc, result) + incl(result.flags, nfTransf) + #if prc.name.s == "testbody": # echo renderTree(result) proc transformStmt*(module: PSym, n: PNode): PNode = @@ -985,10 +986,12 @@ proc transformStmt*(module: PSym, n: PNode): PNode = result = processTransf(c, n, module) liftDefer(c, result) #result = liftLambdasForTopLevel(module, result) - incl(result.flags, nfTransf) when useEffectSystem: trackTopLevelStmt(module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) + if c.needsDestroyPass and newDestructors: + result = injectDestructorCalls(module, result) + incl(result.flags, nfTransf) proc transformExpr*(module: PSym, n: PNode): PNode = if nfTransf in n.flags: @@ -997,4 +1000,6 @@ proc transformExpr*(module: PSym, n: PNode): PNode = var c = openTransf(module, "") result = processTransf(c, n, module) liftDefer(c, result) + if c.needsDestroyPass and newDestructors: + result = injectDestructorCalls(module, result) incl(result.flags, nfTransf) |