diff options
51 files changed, 696 insertions, 598 deletions
diff --git a/changelog.md b/changelog.md index 22d763f53..a4161f504 100644 --- a/changelog.md +++ b/changelog.md @@ -8,3 +8,5 @@ - Arrays of char cannot be converted to ``cstring`` anymore, pointers to arrays of char can! This means ``$`` for arrays can finally exist in ``system.nim`` and do the right thing. +- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour to + `getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added. \ No newline at end of file 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) diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 2c4b019ad..070296271 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -570,7 +570,7 @@ order. The *names* of the fields also have to be identical. The assignment operator for tuples copies each component. The default assignment operator for objects copies each component. Overloading -of the assignment operator is described in `type-bound-operations-operator`_. +of the assignment operator is described in `type-bound-operations-operator`_. .. code-block:: nim @@ -905,8 +905,8 @@ not compatible to ``pointer`` to prevent the following from compiling: Future directions: * Memory regions might become available for ``string`` and ``seq`` too. -* Builtin regions like ``private``, ``global`` and ``local`` will - prove very useful for the upcoming OpenCL target. +* Builtin regions like ``private``, ``global`` and ``local`` might be + useful for an OpenCL target. * Builtin "regions" can model ``lent`` and ``unique`` pointers. * An assignment operator can be attached to a region so that proper write barriers can be generated. This would imply that the GC can be implemented diff --git a/lib/nimbase.h b/lib/nimbase.h index 34a2e43e7..c06c45691 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -396,13 +396,6 @@ typedef struct TStringDesc* string; #define GenericSeqSize sizeof(TGenericSeq) #define paramCount() cmdCount -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__i386__) -# ifndef NAN -static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff}; -# define NAN (*(double*) nimNaN) -# endif -#endif - #ifndef NAN # define NAN (0.0 / 0.0) #endif diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 69f6acfb8..7321889a8 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -146,7 +146,7 @@ type Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int Off* {.importc: "off_t", header: "<sys/types.h>".} = int64 - Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int + Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32 Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int Pthread_barrier* {.importc: "pthread_barrier_t", header: "<sys/types.h>".} = int diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index f847ddd58..560273dfa 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -37,6 +37,14 @@ type DoublyLinkedRing*[T] = object ## a doubly linked ring head*: DoublyLinkedNode[T] + SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T] + + SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T] + + SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T] + + SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T] + {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, PDoublyLinkedNode: DoublyLinkedNode, TSinglyLinkedNode: SinglyLinkedNodeObj, @@ -86,137 +94,57 @@ template itemsRingImpl() {.dirty.} = it = it.next if it == L.head: break -template nodesListImpl() {.dirty.} = - var it = L.head - while it != nil: - var nxt = it.next - yield it - it = nxt - -template nodesRingImpl() {.dirty.} = - var it = L.head - if it != nil: - while true: - var nxt = it.next - yield it - it = nxt - if it == L.head: break - -template findImpl() {.dirty.} = - for x in nodes(L): - if x.value == value: return x - -iterator items*[T](L: DoublyLinkedList[T]): T = +iterator items*[T](L: SomeLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: SinglyLinkedList[T]): T = - ## yields every value of `L`. - itemsListImpl() - -iterator items*[T](L: SinglyLinkedRing[T]): T = - ## yields every value of `L`. - itemsRingImpl() - -iterator items*[T](L: DoublyLinkedRing[T]): T = +iterator items*[T](L: SomeLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() -iterator mitems*[T](L: var DoublyLinkedList[T]): var T = +iterator mitems*[T](L: var SomeLinkedList[T]): var T = ## yields every value of `L` so that you can modify it. itemsListImpl() -iterator mitems*[T](L: var SinglyLinkedList[T]): var T = - ## yields every value of `L` so that you can modify it. - itemsListImpl() - -iterator mitems*[T](L: var SinglyLinkedRing[T]): var T = +iterator mitems*[T](L: var SomeLinkedRing[T]): var T = ## yields every value of `L` so that you can modify it. itemsRingImpl() -iterator mitems*[T](L: var DoublyLinkedRing[T]): var T = - ## yields every value of `L` so that you can modify it. - itemsRingImpl() - -iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] = - ## iterates over every node of `x`. Removing the current node from the - ## list during traversal is supported. - nodesListImpl() - -iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] = - ## iterates over every node of `x`. Removing the current node from the - ## list during traversal is supported. - nodesListImpl() - -iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. - nodesRingImpl() + var it = L.head + while it != nil: + var nxt = it.next + yield it + it = nxt -iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. - nodesRingImpl() + var it = L.head + if it != nil: + while true: + var nxt = it.next + yield it + it = nxt + if it == L.head: break -template dollarImpl() {.dirty.} = +proc `$`*[T](L: SomeLinkedCollection[T]): string = + ## turns a list into its string representation. result = "[" for x in nodes(L): if result.len > 1: result.add(", ") result.add($x.value) result.add("]") -proc `$`*[T](L: SinglyLinkedList[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: DoublyLinkedList[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: SinglyLinkedRing[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: DoublyLinkedRing[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = +proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. - findImpl() - -proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil - -proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil - -proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil + for x in nodes(L): + if x.value == value: return x -proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil @@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = if n.next != nil: n.next.prev = n.prev if n.prev != nil: n.prev.next = n.next - proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). if L.head != nil: diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 2a0dbd2ca..0f23b7e85 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -448,12 +448,22 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} = if selectWorker(readyWorker, fn, data): return for i in 0.. <currentPoolSize: if selectWorker(addr(workersData[i]), fn, data): return + # determine what to do, but keep in mind this is expensive too: # state.calls < maxPoolSize: warmup phase # (state.calls and 127) == 0: periodic check if state.calls < maxPoolSize or (state.calls and 127) == 0: # ensure the call to 'advice' is atomic: if tryAcquire(stateLock): + if currentPoolSize < minPoolSize: + if not workersData[currentPoolSize].initialized: + activateWorkerThread(currentPoolSize) + let w = addr(workersData[currentPoolSize]) + atomicInc currentPoolSize + if selectWorker(w, fn, data): + release(stateLock) + return + case advice(state) of doNothing: discard of doCreateThread: diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index cb4f4f664..de1d332a3 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -883,7 +883,9 @@ proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int, let data = client.socket.recv(sizeToRecv, timeout) else: let data = await client.socket.recv(sizeToRecv) - if data == "": break # We've been disconnected. + if data == "": + client.close() + break # We've been disconnected. readLen.inc(data.len) if keep: @@ -950,6 +952,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient, if length > 0: let recvLen = await client.recvFull(length, client.timeout, true) if recvLen == 0: + client.close() httpError("Got disconnected while trying to read body.") if recvLen != length: httpError("Received length doesn't match expected length. Wanted " & @@ -962,13 +965,20 @@ proc parseBody(client: HttpClient | AsyncHttpClient, if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0": while true: let recvLen = await client.recvFull(4000, client.timeout, true) - if recvLen == 0: break + if recvLen == 0: + client.close() + break when client is AsyncHttpClient: client.bodyStream.complete() else: client.bodyStream.setPosition(0) + # If the server will close our connection, then no matter the method of + # reading the body, we need to close our socket. + if headers.getOrDefault"Connection" == "close": + client.close() + proc parseResponse(client: HttpClient | AsyncHttpClient, getBody: bool): Future[Response | AsyncResponse] {.multisync.} = @@ -984,7 +994,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, line = await client.socket.recvLine(client.timeout) else: line = await client.socket.recvLine() - if line == "": break # We've been disconnected. + if line == "": + # We've been disconnected. + client.close() + break if line == "\c\L": fullyRead = true break @@ -1033,7 +1046,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = if client.currentURL.hostname != url.hostname or client.currentURL.scheme != url.scheme or - client.currentURL.port != url.port: + client.currentURL.port != url.port or + (not client.connected): let isSsl = url.scheme.toLowerAscii() == "https" if isSsl and not defined(ssl): diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 656114fb1..fd7a3af03 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -37,15 +37,15 @@ ## Retrieving the value of a JSON node can then be achieved using one of the ## helper procedures, which include: ## -## * ``getNum`` -## * ``getFNum`` +## * ``getInt`` +## * ``getFloat`` ## * ``getStr`` -## * ``getBVal`` +## * ``getBool`` ## ## To retrieve the value of ``"key"`` you can do the following: ## ## .. code-block:: Nim -## doAssert jsonNode["key"].getFNum() == 3.14 +## doAssert jsonNode["key"].getFloat() == 3.14 ## ## The ``[]`` operator will raise an exception when the specified field does ## not exist. If you wish to avoid this behaviour you can use the ``{}`` @@ -681,14 +681,25 @@ proc getStr*(n: JsonNode, default: string = ""): string = if n.isNil or n.kind != JString: return default else: return n.str -proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = +proc getInt*(n: JsonNode, default: int = 0): int = ## Retrieves the int value of a `JInt JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. if n.isNil or n.kind != JInt: return default + else: return int(n.num) + +proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt = + ## Retrieves the BiggestInt value of a `JInt JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. + if n.isNil or n.kind != JInt: return default else: return n.num -proc getFNum*(n: JsonNode, default: float = 0.0): float = +proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} = + ## Deprecated - use getInt or getBiggestInt instead + getBiggestInt(n, default) + +proc getFloat*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil. @@ -698,13 +709,21 @@ proc getFNum*(n: JsonNode, default: float = 0.0): float = of JInt: return float(n.num) else: return default -proc getBVal*(n: JsonNode, default: bool = false): bool = +proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} = + ## Deprecated - use getFloat instead + getFloat(n, default) + +proc getBool*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. if n.isNil or n.kind != JBool: return default else: return n.bval +proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} = + ## Deprecated - use getBVal instead + getBool(n, default) + proc getFields*(n: JsonNode, default = initOrderedTable[string, JsonNode](4)): OrderedTable[string, JsonNode] = @@ -1342,7 +1361,7 @@ proc getEnum(node: JsonNode, ast: string, T: typedesc): T = # TODO: I shouldn't need this proc. proc convert[T](x: BiggestInt): T = T(x) verifyJsonKind(node, {JInt}, ast) - return convert[T](node.getNum()) + return convert[T](node.getBiggestInt()) else: verifyJsonKind(node, {JString}, ast) return parseEnum[T](node.getStr()) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 8037b31b0..7fd8bbcef 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -184,6 +184,8 @@ when not defined(JS): proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".} proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".} ## computes x to power raised of y. + ## + ## To compute power between integers, use `^` e.g. 2 ^ 6 proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index bf26d2e59..a54556915 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -253,7 +253,7 @@ is performed. for r in collectLinks(body): echo r -In this example both macros are combined seamlessly in order to maximise +In this example both macros are combined seamlessly in order to maximise efficiency and perform different checks. .. code-block:: nim @@ -308,7 +308,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b ## See top level documentation of his module of how ``scanf`` works. template matchBind(parser) {.dirty.} = var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym(parser), input, results[i], idx)) + conds.add newLetStmt(resLen, newCall(bindSym(parser), inp, results[i], idx)) conds.add resLen.notZero conds.add resLen @@ -316,7 +316,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var p = 0 var idx = genSym(nskVar, "idx") var res = genSym(nskVar, "res") - result = newTree(nnkStmtListExpr, newVarStmt(idx, newLit 0), newVarStmt(res, newLit false)) + let inp = genSym(nskLet, "inp") + result = newTree(nnkStmtListExpr, newLetStmt(inp, input), newVarStmt(idx, newLit 0), newVarStmt(res, newLit false)) var conds = newTree(nnkStmtList) var fullMatch = false while p < pattern.len: @@ -325,7 +326,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b case pattern[p] of '$': var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit($pattern[p]), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit($pattern[p]), idx)) conds.add resLen.notZero conds.add resLen of 'w': @@ -347,7 +348,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b error("no float var given for $f") inc i of 's': - conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", input, idx)) + conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx)) conds.add newEmptyNode() conds.add newEmptyNode() of '.': @@ -364,7 +365,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b token.add pattern[q] inc q var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", input, results[i], newLit(token), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx)) conds.add newCall(bindSym"!=", resLen, newLit min) conds.add resLen else: @@ -386,7 +387,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b let expr = pattern.substr(start, p-1) if i < results.len: var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, buildUserCall(expr, input, results[i], idx)) + conds.add newLetStmt(resLen, buildUserCall(expr, inp, results[i], idx)) conds.add newCall(bindSym"!=", resLen, newLit 0) conds.add resLen else: @@ -406,7 +407,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b else: discard inc p let expr = pattern.substr(start, p-1) - conds.add newCall(bindSym"inc", idx, buildUserCall(expr, input, idx)) + conds.add newCall(bindSym"inc", idx, buildUserCall(expr, inp, idx)) conds.add newEmptyNode() conds.add newEmptyNode() else: error("invalid format string") @@ -417,13 +418,13 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b token.add pattern[p] inc p var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit(token), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit(token), idx)) conds.add resLen.notZero conds.add resLen result.add conditionsToIfChain(conds, idx, res, 0) if fullMatch: result.add newCall(bindSym"and", res, - newCall(bindSym">=", idx, newCall(bindSym"len", input))) + newCall(bindSym">=", idx, newCall(bindSym"len", inp))) else: result.add res @@ -684,3 +685,14 @@ when isMainModule: "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", "main c:/users/anwender/projects/nim/lib/system.nim:2620"] doAssert parseGDB(gdbOut) == result + + # bug #6487 + var count = 0 + + proc test(): string = + inc count + result = ",123123" + + var a: int + discard scanf(test(), ",$i", a) + doAssert count == 1 diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 0c4f15c91..7d9c3108b 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -285,7 +285,7 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) = proc runeSubStr*(s: string, pos:int, len:int = int.high): string = ## Returns the UTF-8 substring starting at codepoint pos - ## with len codepoints. If pos or len is negativ they count from + ## with len codepoints. If pos or len is negative they count from ## the end of the string. If len is not given it means the longest ## possible string. ## diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index d8e4ed52f..4b2e4e052 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -278,7 +278,9 @@ proc `/`*(x: Uri, path: string): Uri = result = x if result.path.len == 0: - result.path = path + if path[0] != '/': + result.path = "/" + result.path.add(path) return if result.path[result.path.len-1] == '/': @@ -476,6 +478,11 @@ when isMainModule: let foo = parseUri("http://example.com") / "/baz" doAssert foo.path == "/baz" + # bug found on stream 13/10/17 + block: + let foo = parseUri("http://localhost:9515") / "status" + doAssert $foo == "http://localhost:9515/status" + # isAbsolute tests block: doAssert "www.google.com".parseUri().isAbsolute() == false @@ -515,4 +522,6 @@ when isMainModule: doAssert "https://example.com/about".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true - doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true \ No newline at end of file + doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true + + echo("All good!") \ No newline at end of file diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 78db96e77..19d27e7d2 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -301,13 +301,14 @@ proc pageAddr(p: pointer): PChunk {.inline.} = result = cast[PChunk](cast[ByteAddress](p) and not PageMask) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) -proc writeFreeList(a: MemRegion) = - var it = a.freeChunksList - c_fprintf(stdout, "freeChunksList: %p\n", it) - while it != nil: - c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n", - it, it.next, it.prev, it.size) - it = it.next +when false: + proc writeFreeList(a: MemRegion) = + var it = a.freeChunksList + c_fprintf(stdout, "freeChunksList: %p\n", it) + while it != nil: + c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n", + it, it.next, it.prev, it.size) + it = it.next const nimMaxHeap {.intdefine.} = 0 diff --git a/lib/system/gc.nim b/lib/system/gc.nim index a2ff72a30..21757cf78 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -155,14 +155,15 @@ template setColor(c, col) = else: c.refcount = c.refcount and not colorMask or col -proc writeCell(msg: cstring, c: PCell) = - var kind = -1 - var typName: cstring = "nil" - if c.typ != nil: - kind = ord(c.typ.kind) - when defined(nimTypeNames): - if not c.typ.name.isNil: - typName = c.typ.name +when defined(logGC): + proc writeCell(msg: cstring, c: PCell) = + var kind = -1 + var typName: cstring = "nil" + if c.typ != nil: + kind = ord(c.typ.kind) + when defined(nimTypeNames): + if not c.typ.name.isNil: + typName = c.typ.name when leakDetector: c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n", diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 96c045e6b..016bf5822 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -398,7 +398,7 @@ template afterThreadRuns() = threadDestructionHandlers[i]() when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): - proc deallocOsPages() + proc deallocOsPages() {.rtl.} when defined(boehmgc): type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} diff --git a/tests/closure/t1641.nim b/tests/closure/t1641.nim new file mode 100644 index 000000000..a3e4da367 --- /dev/null +++ b/tests/closure/t1641.nim @@ -0,0 +1,20 @@ +discard """ + output: '''foo 0 +bar 0 +baz''' +""" + +# bug #1641 +proc baz() = + echo "baz" + +proc bar(x: int, p: proc()) = + echo "bar ", x + p() + +proc foo(x: int, p: proc(x: int)) = + echo "foo ", x + p(x) + +let x = 0 +x.foo do(x: int): x.bar do(): baz() diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim index 461914739..81009dd39 100644 --- a/tests/cpp/tget_subsystem.nim +++ b/tests/cpp/tget_subsystem.nim @@ -21,3 +21,11 @@ proc getSubsystem*[T](): ptr T {. let input: ptr Input = getSubsystem[Input]() + +# bug #4910 + +proc foo() = + var ts: array[10, int] + for t in mitems(ts): + t = 123 + diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim new file mode 100644 index 000000000..2250d4772 --- /dev/null +++ b/tests/destructor/tcustomstrings.nim @@ -0,0 +1,96 @@ +discard """ + output: '''foo bar to appendmore here +foo bar to appendmore here +foo bar to appendmore here +foo bar to appendmore here +foo bar to appendmore here +after 16 16''' + cmd: '''nim c --newruntime $file''' +""" + +type + mystring = object + len, cap: int + data: ptr UncheckedArray[char] + +{.this: self.} + +var + allocCount, deallocCount: int + +proc `=destroy`*(self: var mystring) = + if data != nil: + dealloc(data) + inc deallocCount + data = nil + len = 0 + cap = 0 + +proc `=sink`*(a: var mystring, b: mystring) = + # we hope this is optimized away for not yet alive objects: + if a.data != nil and a.data != b.data: + dealloc(a.data) + inc deallocCount + a.len = b.len + a.cap = b.cap + a.data = b.data + +proc `=`*(a: var mystring; b: mystring) = + if a.data != nil and a.data != b.data: + dealloc(a.data) + inc deallocCount + a.data = nil + a.len = b.len + a.cap = b.cap + if b.data != nil: + a.data = cast[type(a.data)](alloc(a.cap + 1)) + inc allocCount + copyMem(a.data, b.data, a.cap+1) + +proc resize(self: var mystring) = + if cap == 0: cap = 8 + else: cap = (cap * 3) shr 1 + if data == nil: inc allocCount + data = cast[type(data)](realloc(data, cap + 1)) + +proc add*(self: var mystring; c: char) = + if self.len >= self.cap: resize(self) + self.data[self.len] = c + self.data[self.len+1] = '\0' + inc self.len + +proc ensure(self: var mystring; newLen: int) = + if newLen >= cap: + cap = max((cap * 3) shr 1, newLen) + if cap > 0: + if data == nil: inc allocCount + data = cast[type(data)](realloc(data, cap + 1)) + +proc add*(self: var mystring; y: mystring) = + let newLen = len + y.len + ensure(self, newLen) + copyMem(addr data[len], y.data, y.data.len + 1) + len = newLen + +proc create*(lit: string): mystring = + let newLen = lit.len + ensure(result, newLen) + copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1) + result.len = newLen + +proc `&`*(a, b: mystring): mystring = + result = a + result.add b + +proc main(n: int) = + var a: mystring + let b = create" to append" + for i in 0..<n: + if i > 4: break + a = create"foo bar" + let c = b & create"more here" + a.add c + echo cstring(a.data) + +main(1000) +echo "after ", allocCount, " ", deallocCount diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim new file mode 100644 index 000000000..0b2c090a7 --- /dev/null +++ b/tests/effects/tgcsafe2.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: '''type mismatch: got (proc (s: string){.locks: 0.})''' + line: 11 +""" +#5620 +var res = "" + +proc takeCallback(foo: (proc(s: string) {.gcsafe.})) = + foo "string" + +takeCallback(proc (s: string) = + res &= s & "abc") diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 32d848e06..153cf8556 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -87,8 +87,8 @@ when isMainModule: result = to(node, TestVariant) doAssert result.name == node["name"].getStr() - doAssert result.age == node["age"].getNum().uint8 - doAssert result.other == node["other"].getNum() + doAssert result.age == node["age"].getInt().uint8 + doAssert result.other == node["other"].getBiggestInt() # TODO: Test object variant with set in of branch. # TODO: Should we support heterogenous arrays? diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index a93c79f5c..d620a4587 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -83,9 +83,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = "" testSpec c, makeTest("lib/nimrtl.nim", - options & " --app:lib -d:createNimRtl", cat) + options & " --app:lib -d:createNimRtl --threads:on", cat) testSpec c, makeTest("tests/dll/server.nim", - options & " --app:lib -d:useNimRtl" & rpath, cat) + options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat) when defined(Windows): @@ -101,7 +101,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = var nimrtlDll = DynlibFormat % "nimrtl" safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) - testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl" & rpath, + testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath, cat, actionRun) proc dllTests(r: var TResults, cat: Category, options: string) = diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim index 2c967b1e8..36d1382cf 100644 --- a/tools/nimpretty.nim +++ b/tools/nimpretty.nim @@ -42,7 +42,8 @@ proc writeVersion() = proc prettyPrint(infile: string) = let fileIdx = fileInfoIdx(infile) let tree = parseFile(fileIdx, newIdentCache()) - renderModule(tree, infile, {}) + let outfile = changeFileExt(infile, ".pretty.nim") + renderModule(tree, infile, outfile, {}) proc main = var infile: string @@ -50,7 +51,7 @@ proc main = for kind, key, val in getopt(): case kind of cmdArgument: - infile = key + infile = key.addFileExt(".nim") of cmdLongoption, cmdShortOption: case normalize(key) of "help", "h": writeHelp() diff --git a/web/bountysource.nim b/web/bountysource.nim deleted file mode 100644 index 5dfdb4497..000000000 --- a/web/bountysource.nim +++ /dev/null @@ -1,140 +0,0 @@ -# Based on bountysource.cr located at https://github.com/crystal-lang/crystal-website/blob/master/scripts/bountysource.cr -import httpclient, asyncdispatch, json, strutils, os, strtabs, sequtils, future, - algorithm, times - -type - BountySource = ref object - client: AsyncHttpClient - team: string - - Sponsor = object - name, url, logo: string - amount, allTime: float - since: TimeInfo - -const - team = "nim" - apiUrl = "https://api.bountysource.com" - githubApiUrl = "https://api.github.com" - -proc newBountySource(team, token: string): BountySource = - result = BountySource( - client: newAsyncHttpClient(userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36"), - team: team - ) - - # Set up headers - result.client.headers["Accept"] = "application/vnd.bountysource+json; version=2" - result.client.headers["Authorization"] = "token " & token - result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters" - result.client.headers["Origin"] = "https://salt.bountysource.com/" - -proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = - let response = await self.client.get(apiUrl & - "/supporters?order=monthly&per_page=200&team_slug=" & self.team) - doAssert response.status.startsWith($Http200) - return parseJson(await response.body) - -proc getGithubUser(username: string): Future[JsonNode] {.async.} = - let client = newAsyncHttpClient() - let response = await client.get(githubApiUrl & "/users/" & username) - if response.status.startsWith($Http200): - return parseJson(await response.body) - else: - echo("Could not get Github user: ", username, ". ", response.status) - return nil - -proc processSupporters(supporters: JsonNode) = - var before = supporters.elems.len - supporters.elems.keepIf( - item => item["display_name"].getStr != "Anonymous" - ) - echo("Discarded ", before - supporters.elems.len, " anonymous sponsors.") - echo("Found ", supporters.elems.len, " named sponsors.") - - supporters.elems.sort( - (x, y) => cmp(y["alltime_amount"].getFNum, x["alltime_amount"].getFNum) - ) - - -proc quote(text: string): string = - if {' ', ','} in text: - return "\"" & text & "\"" - else: - return text - -proc getLevel(amount: float): int = - result = 0 - const levels = [250, 150, 75, 25, 10, 5, 1] - for i in levels: - if amount.int <= i: - result = i - -proc writeCsv(sponsors: seq[Sponsor], filename="sponsors.new.csv") = - var csv = "" - csv.add "logo, name, url, this_month, all_time, since, level\n" - for sponsor in sponsors: - csv.add "$#,$#,$#,$#,$#,$#,$#\n" % [ - sponsor.logo.quote, sponsor.name.quote, - sponsor.url.quote, $sponsor.amount.int, - $sponsor.allTime.int, sponsor.since.format("MMM d, yyyy").quote, - $sponsor.amount.getLevel - ] - writeFile(filename, csv) - echo("Written csv file to ", filename) - -when isMainModule: - if paramCount() == 0: - quit("You need to specify the BountySource access token on the command\n" & - "line, you can find it by going onto https://www.bountysource.com/people/25278-dom96\n" & - "and looking at your browser's network inspector tab to see the token being\n" & - "sent to api.bountysource.com") - - let token = paramStr(1) - let bountysource = newBountySource(team, token) - - echo("Getting sponsors...") - let supporters = waitFor bountysource.getSupporters() - processSupporters(supporters) - - echo("Generating sponsors list... (please be patient)") - var activeSponsors: seq[Sponsor] = @[] - var inactiveSponsors: seq[Sponsor] = @[] - for supporter in supporters: - let name = supporter["display_name"].getStr - var url = "" - let ghUser = waitFor getGithubUser(name) - if not ghUser.isNil: - if ghUser["blog"].kind != JNull: - url = ghUser["blog"].getStr - else: - url = ghUser["html_url"].getStr - - if url.len > 0 and not url.startsWith("http"): - url = "http://" & url - - let amount = supporter["monthly_amount"].getFNum() - # Only show URL when user donated at least $5. - if amount < 5: - url = "" - - #let supporter = getSupporter(supporters, - # supportLevel["owner"]["display_name"].getStr) - #if supporter.isNil: continue - var logo = "" - if amount >= 75: - discard # TODO - - let sponsor = Sponsor(name: name, url: url, logo: logo, amount: amount, - allTime: supporter["alltime_amount"].getFNum(), - since: parse(supporter["created_at"].getStr, "yyyy-MM-dd'T'hh:mm:ss") - ) - if supporter["monthly_amount"].getFNum > 0.0: - activeSponsors.add(sponsor) - else: - inactiveSponsors.add(sponsor) - - echo("Generated ", activeSponsors.len, " active sponsors") - echo("Generated ", inactiveSponsors.len, " inactive sponsors") - writeCsv(activeSponsors) - writeCsv(inactiveSponsors, "inactive_sponsors.new.csv") diff --git a/web/bountysource.nim.cfg b/web/bountysource.nim.cfg deleted file mode 100644 index 521e21de4..000000000 --- a/web/bountysource.nim.cfg +++ /dev/null @@ -1 +0,0 @@ --d:ssl diff --git a/web/website.ini b/web/website.ini index 13e9b97e8..b91e4003a 100644 --- a/web/website.ini +++ b/web/website.ini @@ -35,7 +35,7 @@ doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst" pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst" srcdoc2: "system.nim;system/nimscript;pure/ospaths" srcdoc2: "core/macros;pure/marshal;core/typeinfo" -srcdoc2: "impure/re;impure/nre;pure/typetraits" +srcdoc2: "impure/re;impure/nre;pure/typetraits;../nimsuggest/sexp.nim" srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim" srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" |