diff options
81 files changed, 1182 insertions, 316 deletions
diff --git a/changelog.md b/changelog.md index 6844d3791..b8d28b633 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,8 @@ - ``re.split`` now also supports the ``maxsplit`` parameter for consistency with ``strutils.split``. +- Added ``system.toOpenArray`` in order to support zero-copy slicing + operations. This is currently not yet available for the JavaScript target. ### Library changes @@ -20,6 +22,11 @@ ### Language changes +- The `importcpp` pragma now allows importing the listed fields of generic + C++ types. Support for numeric parameters have also been added through + the use of `static[T]` types. + (#6415) + ### Tool changes - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed @@ -27,9 +34,5 @@ ### Compiler changes -### Bugfixes - -- The `importcpp` pragma now allows importing the listed fields of generic - C++ types. Support for numeric parameters have also been added through - the use of `static[T]` types. - (#6415) +- The VM's instruction count limit was raised to 1 billion instructions in order + to support more complex computations at compile-time. diff --git a/compiler/ast.nim b/compiler/ast.nim index 8286e3bb7..a28a7e7e3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -981,6 +981,7 @@ const nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit} + nkFloatLiterals* = {nkFloatLit..nkFloat128Lit} nkLambdaKinds* = {nkLambda, nkDo} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs* = nkLambdaKinds + declarativeDefs @@ -1476,7 +1477,7 @@ proc copyNode*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1495,7 +1496,7 @@ proc shallowCopy*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1515,7 +1516,7 @@ proc copyTree*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1564,7 +1565,7 @@ proc getInt*(a: PNode): BiggestInt = proc getFloat*(a: PNode): BiggestFloat = case a.kind - of nkFloatLit..nkFloat128Lit: result = a.floatVal + of nkFloatLiterals: result = a.floatVal else: internalError(a.info, "getFloat") result = 0.0 diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 18741732c..5f14b6804 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -83,6 +83,8 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = result = isInCurrentFrame(p, n.sons[0]) else: discard +proc genIndexCheck(p: BProc; arr, idx: TLoc) + proc openArrayLoc(p: BProc, n: PNode): Rope = var a: TLoc @@ -93,18 +95,28 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = initLocExpr(p, q[1], a) initLocExpr(p, q[2], b) initLocExpr(p, q[3], c) - let fmt = - case skipTypes(a.t, abstractVar+{tyPtr}).kind - of tyOpenArray, tyVarargs, tyArray: - "($1)+($2), ($3)-($2)+1" - of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar and - not compileToCpp(p.module): - "(*$1)->data+($2), ($3)-($2)+1" - else: - "$1->data+($2), ($3)-($2)+1" - else: (internalError("openArrayLoc: " & typeToString(a.t)); "") - result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)] + # but first produce the required index checks: + if optBoundsCheck in p.options: + genIndexCheck(p, a, b) + genIndexCheck(p, a, c) + let ty = skipTypes(a.t, abstractVar+{tyPtr}) + case ty.kind + of tyArray: + let first = firstOrd(ty) + if first == 0: + result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + result = "($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first)] + of tyOpenArray, tyVarargs: + result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + of tyString, tySequence: + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): + result = "(*$1)->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + result = "$1->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + internalError("openArrayLoc: " & typeToString(a.t)) else: initLocExpr(p, n, a) case skipTypes(a.t, abstractVar).kind diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ba0820e93..dc06c8482 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -880,6 +880,23 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = putIntoDest(p, d, n, rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) +proc genIndexCheck(p: BProc; arr, idx: TLoc) = + let ty = skipTypes(arr.t, abstractVarRange) + case ty.kind + of tyOpenArray, tyVarargs: + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", + rdLoc(idx), rdLoc(arr)) + of tyArray: + let first = intLiteral(firstOrd(ty)) + if tfUncheckedArray notin ty.flags: + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", + rdCharLoc(idx), first, intLiteral(lastOrd(ty))) + of tySequence, tyString: + linefmt(p, cpsStmts, + "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(idx), rdLoc(arr), lenField(p)) + else: discard + proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) @@ -1364,6 +1381,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = rfmt(nil, "(*$1)", r) t = skipTypes(t.lastSon, typedescInst) + discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ~".Sup") @@ -1910,7 +1928,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = if getSize(e.typ) > 8: # big set: useStringh(p.module) - lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)]) + lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n", + [rdLoc(d), getTypeDesc(p.module, e.typ)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: getTemp(p, getSysType(tyInt), idx) # our counter @@ -2042,6 +2061,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = "(*$1)" % [r] t = skipTypes(t.lastSon, abstractInst) + discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ".Sup") @@ -2061,6 +2081,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: + discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) @@ -2069,6 +2090,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = while arg.kind == nkObjDownConv: arg = arg.sons[0] var src = skipTypes(arg.typ, abstractPtrs) + discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6b8ba2b02..f6c4204e8 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -37,6 +37,10 @@ proc isAssignedImmediately(n: PNode): bool {.inline.} = return false result = true +proc inExceptBlockLen(p: BProc): int = + for x in p.nestedTryStmts: + if x.inExcept: result.inc + proc genVarTuple(p: BProc, n: PNode) = var tup, field: TLoc if n.kind != nkVarTuple: internalError(n.info, "genVarTuple") @@ -96,7 +100,7 @@ proc startBlock(p: BProc, start: FormatStr = "{$n", setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 - p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 + p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 proc assignLabel(b: var TBlock): Rope {.inline.} = b.label = "LA" & b.id.rope @@ -344,26 +348,22 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = # Called by return and break stmts. # Deals with issues faced when jumping out of try/except/finally stmts, - var stack: seq[PNode] - newSeq(stack, 0) + var stack = newSeq[tuple[n: PNode, inExcept: bool]](0) - var alreadyPoppedCnt = p.inExceptBlock for i in countup(1, howManyTrys): + let tryStmt = p.nestedTryStmts.pop if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions: # Pop safe points generated by try - if alreadyPoppedCnt > 0: - dec alreadyPoppedCnt - else: + if not tryStmt.inExcept: linefmt(p, cpsStmts, "#popSafePoint();$n") # Pop this try-stmt of the list of nested trys # so we don't infinite recurse on it in the next step. - var tryStmt = p.nestedTryStmts.pop stack.add(tryStmt) # Find finally-stmt for this try-stmt # and generate a copy of its sons - var finallyStmt = lastSon(tryStmt) + var finallyStmt = lastSon(tryStmt.n) if finallyStmt.kind == nkFinally: genStmts(p, finallyStmt.sons[0]) @@ -384,7 +384,7 @@ proc genReturnStmt(p: BProc, t: PNode) = if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, - howManyExcepts = p.inExceptBlock) + howManyExcepts = p.inExceptBlockLen) if (p.finallySafePoints.len > 0): # If we're in a finally block, and we came here by exception # consume it before we return. @@ -567,15 +567,15 @@ proc genBreakStmt(p: BProc, t: PNode) = let label = assignLabel(p.blocks[idx]) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, - p.inExceptBlock - p.blocks[idx].nestedExceptStmts) + p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) lineF(p, cpsStmts, "goto $1;$n", [label]) proc genRaiseStmt(p: BProc, t: PNode) = - if p.inExceptBlock > 0 and p.inExceptBlock == p.nestedTryStmts.len: + if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, # we must execute it before reraising - var finallyBlock = p.nestedTryStmts[p.nestedTryStmts.len - 1].lastSon + var finallyBlock = p.nestedTryStmts[^1].n[^1] if finallyBlock.kind == nkFinally: genSimpleBlock(p, finallyBlock.sons[0]) if t.sons[0].kind != nkEmpty: @@ -812,14 +812,14 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = let end_label = getLabel(p) discard cgsym(p.module, "Exception") - add(p.nestedTryStmts, t) + add(p.nestedTryStmts, (t, false)) startBlock(p, "try {$n") expr(p, t[0], d) endBlock(p) var catchAllPresent = false - inc p.inExceptBlock + p.nestedTryStmts[^1].inExcept = true for i in 1..<t.len: if t[i].kind != nkExceptBranch: break @@ -839,6 +839,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type endBlock(p) + discard pop(p.nestedTryStmts) + if not catchAllPresent and t[^1].kind == nkFinally: # finally requires catch all presence startBlock(p, "catch (...) {$n") @@ -846,9 +848,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = line(p, cpsStmts, ~"throw;$n") endBlock(p) - dec p.inExceptBlock - discard pop(p.nestedTryStmts) - if t[^1].kind == nkFinally: genSimpleBlock(p, t[^1][0]) @@ -902,7 +901,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) - add(p.nestedTryStmts, t) + add(p.nestedTryStmts, (t, false)) expr(p, t.sons[0], d) linefmt(p, cpsStmts, "#popSafePoint();$n") endBlock(p) @@ -910,7 +909,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popSafePoint();$n") if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") - inc p.inExceptBlock + p.nestedTryStmts[^1].inExcept = true var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: @@ -941,7 +940,6 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) inc(i) - dec p.inExceptBlock discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: @@ -965,6 +963,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = elif sym.kind == skType: res.add($getTypeDesc(p.module, sym.typ)) else: + discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs)) var r = sym.loc.r if r == nil: # if no name has already been given, @@ -975,10 +974,10 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = of nkTypeOfExpr: res.add($getTypeDesc(p.module, t.sons[i].typ)) else: + discard getTypeDesc(p.module, skipTypes(t[i].typ, abstractPtrs)) var a: TLoc - initLocExpr(p, t.sons[i], a) + initLocExpr(p, t[i], a) res.add($a.rdLoc) - #internalError(t.sons[i].info, "genAsmOrEmitStmt()") if isAsmStmt and hasGnuAsm in CC[cCompiler].props: for x in splitLines(res): diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bdba34e36..ed44c577d 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -368,6 +368,8 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = if not isImportedType(concrete): addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(typ), result]) + else: + pushType(m, concrete) doAssert m.forwTypeCache[sig] == result else: internalError("getTypeForward(" & $typ.kind & ')') @@ -665,7 +667,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = let name = getTypeForward(m, et, hashType et) result = name & star m.typeCache[sig] = result - pushType(m, et) of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) @@ -841,6 +842,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # always call for sideeffects: assert t.kind != tyTuple discard getRecordDesc(m, t, result, check) + # The resulting type will include commas and these won't play well + # with the C macros for defining procs such as N_NIMCALL. We must + # create a typedef for the type and use it in the proc signature: + let typedefName = ~"TY" & $sig + addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName]) + m.typeCache[sig] = typedefName + result = typedefName else: when false: if t.sym != nil and t.sym.name.s == "KeyValuePair": diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8b3da223f..01610010b 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -317,8 +317,10 @@ proc resetLoc(p: BProc, loc: var TLoc) = genObjectInit(p, cpsStmts, loc.t, loc, true) else: useStringh(p.module) + # array passed as argument decayed into pointer, bug #7332 + # so we use getTypeDesc here rather than rdLoc(loc) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, loc.t)) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, true) @@ -335,7 +337,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = if not isImportedCppType(typ): useStringh(p.module) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, typ)) genObjectInit(p, cpsStmts, loc.t, loc, true) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0f8fa760e..efa346934 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -70,11 +70,10 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous - nestedTryStmts*: seq[PNode] # in how many nested try statements we are - # (the vars must be volatile then) - inExceptBlock*: int # are we currently inside an except block? - # leaving such scopes by raise or by return must - # execute any applicable finally blocks + nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] + # in how many nested try statements we are + # (the vars must be volatile then) + # bool is true when are in the except part of a try block finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when # using return in finally statements labels*: Natural # for generating unique labels in the C proc diff --git a/compiler/commands.nim b/compiler/commands.nim index 766b78798..2e9b6f8db 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -257,6 +257,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "rangechecks": result = contains(gOptions, optRangeCheck) of "boundchecks": result = contains(gOptions, optBoundsCheck) of "overflowchecks": result = contains(gOptions, optOverflowCheck) + of "movechecks": result = contains(gOptions, optMoveCheck) of "linedir": result = contains(gOptions, optLineDir) of "assertions", "a": result = contains(gOptions, optAssert) of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim) @@ -493,6 +494,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info) of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info) of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info) + of "movechecks": processOnOffSwitch({optMoveCheck}, arg, pass, info) of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 55da69985..ffa3e37e5 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -89,11 +89,34 @@ ## tmp.bar)) ## destroy(tmp.bar) ## destroy(tmp.x); destroy(tmp.y) +## + +##[ +From https://github.com/nim-lang/Nim/wiki/Destructors + +Rule Pattern Transformed into +---- ------- ---------------- +1.1 var x: T; stmts var x: T; try stmts + finally: `=destroy`(x) +1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) +2 x = f() `=sink`(x, f()) +3 x = lastReadOf z `=sink`(x, z) +4.1 y = sinkParam `=sink`(y, sinkParam) +4.2 x = y `=`(x, y) # a copy +5.1 f_sink(g()) f_sink(g()) +5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read +5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) +Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently + not allowed as a local variable. + +``move`` builtin needs to be implemented. +]## import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - strutils, options, dfa, lowerings, rodread + strutils, options, dfa, lowerings, rodread, tables const InterestingSyms = {skVar, skResult, skLet} @@ -106,6 +129,14 @@ type tmpObj: PType tmp: PSym destroys, topLevelVars: PNode + toDropBit: Table[int, PSym] + +proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = + # XXX why are temps fields in an object here? + let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info) + f.typ = typ + rawAddField c.tmpObj, f + result = rawDirectAccess(c.tmp, f) proc isHarmlessVar*(s: PSym; c: Con): bool = # 's' is harmless if it used only once and its @@ -212,14 +243,44 @@ proc genDestroy(t: PType; dest: PNode): PNode = proc addTopVar(c: var Con; v: PNode) = c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) +proc dropBit(c: var Con; s: PSym): PSym = + result = c.toDropBit.getOrDefault(s.id) + assert result != nil + +proc registerDropBit(c: var Con; s: PSym) = + let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info) + result.typ = getSysType(tyBool) + let trueVal = newIntTypeNode(nkIntLit, 1, result.typ) + c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal) + c.toDropBit[s.id] = result + # generate: + # if not sinkParam_AliveBit: `=destroy`(sinkParam) + c.destroys.add newTree(nkIfStmt, + newTree(nkElifBranch, newSymNode result, genDestroy(s.typ, newSymNode s))) + 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 isSinkParam(s: PSym): bool {.inline.} = + result = s.kind == skParam and s.typ.kind == tySink + +const constrExprs = nkCallKinds+{nkObjConstr} + +proc destructiveMoveSink(n: PNode; c: var Con): PNode = + # generate: (chckMove(sinkParam_AliveBit); sinkParam_AliveBit = false; sinkParam) + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let bit = newSymNode dropBit(c, n.sym) + if optMoveCheck in c.owner.options: + result.add callCodegenProc("chckMove", bit) + result.add newTree(nkAsgn, bit, + newIntTypeNode(nkIntLit, 0, getSysType(tyBool))) + result.add n + proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = - if ri.kind in nkCallKinds+{nkObjConstr}: + if ri.kind in constrExprs: result = genSink(ri.typ, dest) # watch out and no not transform 'ri' twice if it's a call: let ri2 = copyNode(ri) @@ -228,10 +289,51 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): result = genSink(ri.typ, dest) result.add p(ri, c) + elif ri.kind == nkSym and isSinkParam(ri.sym): + result = genSink(ri.typ, dest) + result.add destructiveMoveSink(ri, c) else: result = genCopy(ri.typ, dest) result.add p(ri, c) +proc passCopyToSink(n: PNode; c: var Con): PNode = + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let tmp = getTemp(c, n.typ, n.info) + if hasDestructor(n.typ): + var m = genCopy(n.typ, tmp) + m.add p(n, c) + result.add m + message(n.info, hintPerformance, + "passing '$1' to a sink parameter introduces an implicit copy; " & + "use 'move($1)' to prevent it" % $n) + else: + result.add newTree(nkAsgn, tmp, p(n, c)) + result.add tmp + +proc genReset(n: PNode; c: var Con): PNode = + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic("reset", mReset))) + # The mReset builtin does not take the address: + result.add n + +proc destructiveMoveVar(n: PNode; c: var Con): PNode = + # generate: (let tmp = v; reset(v); tmp) + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + + var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info) + var v = newNodeI(nkLetSection, n.info) + let tempAsNode = newSymNode(temp) + + var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) + vpart.sons[0] = tempAsNode + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = n + add(v, vpart) + + result.add v + result.add genReset(n, c) + result.add tempAsNode + proc p(n: PNode; c: var Con): PNode = case n.kind of nkVarSection, nkLetSection: @@ -266,22 +368,45 @@ proc p(n: PNode; c: var Con): PNode = varSection.add itCopy result.add varSection of nkCallKinds: + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + for i in 1 ..< n.len: + let arg = n[i] + if i < L and parameters[i].kind == tySink: + if arg.kind in nkCallKinds: + # recurse but skip the call expression in order to prevent + # destructor injections: Rule 5.1 is different from rule 5.4! + let a = copyNode(arg) + recurse(arg, a) + n.sons[i] = a + elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}: + discard "object construction to sink parameter: nothing to do" + elif arg.kind == nkSym and isHarmlessVar(arg.sym, c): + # if x is a variable and it its last read we eliminate its + # destructor invokation, but don't. We need to reset its memory + # to disable its destructor which we have not elided: + n.sons[i] = destructiveMoveVar(arg, c) + elif arg.kind == nkSym and isSinkParam(arg.sym): + # mark the sink parameter as used: + n.sons[i] = destructiveMoveSink(arg, c) + else: + # an object that is not temporary but passed to a 'sink' parameter + # results in a copy. + n.sons[i] = passCopyToSink(arg, c) + else: + n.sons[i] = p(arg, c) + 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)) + let tmp = getTemp(c, n.typ, n.info) + var sinkExpr = genSink(n.typ, tmp) + sinkExpr.add n + result.add sinkExpr + result.add tmp + c.destroys.add genDestroy(n.typ, tmp) else: - result = copyNode(n) - recurse(n, result) + result = n of nkAsgn, nkFastAsgn: if hasDestructor(n[0].typ): result = moveOrCopy(n[0], n[1], c) @@ -311,6 +436,11 @@ proc injectDestructorCalls*(owner: PSym; n: PNode): PNode = for i in 0..<c.g.len: if c.g[i].kind in {goto, fork}: c.jumpTargets.incl(i+c.g[i].dest) + if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}: + let params = owner.typ.n + for i in 1 ..< params.len: + let param = params[i].sym + if param.typ.kind == tySink: registerDropBit(c, param) let body = p(n, c) if c.tmp.typ.n.len > 0: c.addTopVar(newSymNode c.tmp) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index fca5ef52e..bc05ec80c 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -275,8 +275,12 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode = proc markAsClosure(owner: PSym; n: PNode) = let s = n.sym - if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}: - localError(n.info, errIllegalCaptureX, s.name.s) + if illegalCapture(s): + localError(n.info, "illegal capture '$1' of type <$2> which is declared here: $3" % + [s.name.s, typeToString(s.typ), $s.info]) + elif owner.typ.callConv notin {ccClosure, ccDefault}: + localError(n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % + [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) incl(owner.typ.flags, tfCapturesEnv) owner.typ.callConv = ccClosure diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c409acc59..0675c5ca0 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -44,6 +44,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = case x.kind of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) + of nkLiterals - nkFloatLiterals: id.add(x.renderTree) else: handleError(n, origin) result = getIdent(id) of nkOpenSymChoice, nkClosedSymChoice: @@ -456,4 +457,4 @@ proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; a = nextOverloadIter(o, c, n) proc isInfixAs*(n: PNode): bool = - return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" \ No newline at end of file + return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" diff --git a/compiler/msgs.nim b/compiler/msgs.nim index ac4242e67..70504cfc9 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -134,7 +134,7 @@ type hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintConditionAlwaysTrue, hintName, hintPattern, hintExecuting, hintLinking, hintDependency, - hintSource, hintStackTrace, hintGCStats, + hintSource, hintPerformance, hintStackTrace, hintGCStats, hintUser, hintUserRaw const @@ -357,7 +357,9 @@ const errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", - errTooManyIterations: "interpretation requires too many iterations", + errTooManyIterations: "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler", errCannotInterpretNodeX: "cannot evaluate \'$1\'", errFieldXNotFound: "field \'$1\' cannot be found", errInvalidConversionFromTypeX: "invalid conversion from type \'$1\'", @@ -436,6 +438,7 @@ const hintLinking: "", hintDependency: "$1", hintSource: "$1", + hintPerformance: "$1", hintStackTrace: "$1", hintGCStats: "$1", hintUser: "$1", @@ -458,7 +461,7 @@ const "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "StackTrace", "GCStats", + "Source", "Performance", "StackTrace", "GCStats", "User", "UserRaw"] const diff --git a/compiler/options.nim b/compiler/options.nim index 65f5a6245..be13e15d7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -25,7 +25,7 @@ type # please make sure we have under 32 options TOption* = enum # **keep binary compatible** optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, - optNaNCheck, optInfCheck, + optNaNCheck, optInfCheck, optMoveCheck, optAssert, optLineDir, optWarns, optHints, optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optLineTrace, # line tracing support (includes stack tracing) @@ -119,13 +119,14 @@ var const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, - optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} + optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck, + optMoveCheck} var gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, - optPatterns, optNilCheck} + optPatterns, optNilCheck, optMoveCheck} gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} gExitcode*: int8 gCmd*: TCommands = cmdNone # the command diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index aacd25795..8540f1b32 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -178,6 +178,36 @@ type arDiscriminant, # is a discriminant arStrange # it is a strange beast like 'typedesc[var T]' +proc exprRoot*(n: PNode): PSym = + var it = n + while true: + case it.kind + of nkSym: return it.sym + of nkHiddenDeref, nkDerefExpr: + if it[0].typ.skipTypes(abstractInst).kind in {tyPtr, tyRef}: + # 'ptr' is unsafe anyway and 'ref' is always on the heap, + # so allow these derefs: + break + else: + it = it[0] + of nkDotExpr, nkBracketExpr, nkHiddenAddr, + nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + it = it[0] + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + it = it[1] + of nkStmtList, nkStmtListExpr: + if it.len > 0 and it.typ != nil: it = it.lastSon + else: break + of nkCallKinds: + if it.typ != nil and it.typ.kind == tyVar and it.len > 1: + # See RFC #7373, calls returning 'var T' are assumed to + # return a view into the first argument (if there is one): + it = it[1] + else: + break + else: + break + proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone @@ -189,7 +219,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet} else: {skVar, skResult, skTemp} if n.sym.kind in kinds: - if owner != nil and owner.id == n.sym.owner.id and + if owner != nil and owner == n.sym.owner and sfGlobal notin n.sym.flags: result = arLocalLValue else: diff --git a/compiler/parser.nim b/compiler/parser.nim index 1ae7646cd..debb0b34d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -820,7 +820,6 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = if realInd(p): p.currInd = p.tok.indent wasIndented = true - echo result.info, " yes ", p.currInd addSon(branch, parseExpr(p)) result.add branch while sameInd(p) or not wasIndented: @@ -964,6 +963,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + elif p.tok.tokType == tkParLe: + parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") else: result = newNodeP(nkTupleClassTy, p) @@ -985,6 +986,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = a = parseIdentColonEquals(p, {withBothOptional, withPragma}) of tkParRi: break + of tkVar: + parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'") + break else: parMessage(p, errTokenExpected, ")") break @@ -2132,7 +2136,12 @@ proc parseTopLevelStmt(p: var TParser): PNode = if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard elif p.tok.tokType != tkSemiColon: - parMessage(p, errInvalidIndentation) + # special casing for better error messages: + if p.tok.tokType == tkOpr and p.tok.ident.s == "*": + parMessage(p, errGenerated, + "invalid indentation; an export marker '*' follows the declared identifier") + else: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1b8078628..1295ee18d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -40,7 +40,8 @@ const wTags, wLocks, wGcSafe, wExportNims, wUsed} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, - wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, + wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, + wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, @@ -323,6 +324,7 @@ proc processOption(c: PContext, n: PNode): bool = of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck}) of wNanChecks: onOff(c, n, {optNaNCheck}) of wInfChecks: onOff(c, n, {optInfCheck}) + of wMovechecks: onOff(c, n, {optMoveCheck}) of wAssertions: onOff(c, n, {optAssert}) of wWarnings: onOff(c, n, {optWarns}) of wHints: onOff(c, n, {optHints}) @@ -693,7 +695,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, inc c.instCounter if c.instCounter > 100: globalError(it.info, errRecursiveDependencyX, userPragma.name.s) - + pragma(c, sym, userPragma.ast, validPragmas) n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty @@ -906,7 +908,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, + wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, wPatterns: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index ba38ed215..0fc12f164 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -190,12 +190,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if wanted != nil and got != nil: effectProblem(wanted, got, candidates) if cond: candidates.add "\n" - elif err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: - add(candidates, "for a 'var' type a variable needs to be passed, but '" & + if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: + candidates.add(" for a 'var' type a variable needs to be passed, but '" & renderNotLValue(n[err.unmatchedVarParam]) & "' is immutable\n") for diag in err.diagnostics: - add(candidates, diag & "\n") + candidates.add(diag & "\n") result = (prefer, candidates) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8e3aeffbe..a523bfc9e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -192,6 +192,10 @@ proc semConv(c: PContext, n: PNode): PNode = result.addSon copyTree(n.sons[0]) + # special case to make MyObject(x = 3) produce a nicer error message: + if n[1].kind == nkExprEqExpr and + targetType.skipTypes(abstractPtrs).kind == tyObject: + localError(n.info, "object contruction uses ':', not '='") var op = semExprWithType(c, n.sons[1]) if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) @@ -880,6 +884,9 @@ const proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = + # Note: This function will return emptyNode when attempting to read + # a static type parameter that is not yet resolved (e.g. this may + # happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]` if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}: for statement in typ.n: case statement.kind @@ -910,7 +917,10 @@ proc readTypeParameter(c: PContext, typ: PType, if tParam.sym.name.id == paramName.id: let rawTyp = ty.sons[s + 1] if rawTyp.kind == tyStatic: - return rawTyp.n + if rawTyp.n != nil: + return rawTyp.n + else: + return emptyNode else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) @@ -1075,21 +1085,43 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = template tryReadingGenericParam(t: PType) = case t.kind of tyTypeParamsHolders: - return readTypeParameter(c, t, i, n.info) + result = readTypeParameter(c, t, i, n.info) + if result == emptyNode: + result = n + n.typ = makeTypeFromExpr(c, n.copyTree) + return of tyUserTypeClasses: if t.isResolvedUserTypeClass: return readTypeParameter(c, t, i, n.info) else: n.typ = makeTypeFromExpr(c, copyTree(n)) return n - of tyGenericParam: + of tyGenericParam, tyAnything: n.typ = makeTypeFromExpr(c, copyTree(n)) return n else: discard - if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): - if ty.kind == tyTypeDesc: ty = ty.base + var argIsType = false + + if ty.kind == tyTypeDesc: + if ty.base.kind == tyNone: + # This is a still unresolved typedesc parameter. + # If this is a regular proc, then all bets are off and we must return + # tyFromExpr, but when this happen in a macro this is not a built-in + # field access and we leave the compiler to compile a normal call: + if getCurrOwner(c).kind != skMacro: + n.typ = makeTypeFromExpr(c, n.copyTree) + return n + else: + return nil + else: + ty = ty.base + argIsType = true + else: + argIsType = isTypeExpr(n.sons[0]) + + if argIsType: ty = ty.skipTypes(tyDotOpTransparent) case ty.kind of tyEnum: @@ -1297,13 +1329,23 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = #analyseIfAddressTakenInCall(c, result) proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = + # See RFC #7373, calls returning 'var T' are assumed to + # return a view into the first argument (if there is one): + let root = exprRoot(n) + if root != nil and root.owner == c.p.owner: + if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags: + localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [ + root.name.s, renderTree(n, {renderNoComments})]) + elif root.kind == skParam and root.position != 0: + localError(n.info, "'$1' is not the first parameter; context: '$2'" % [ + root.name.s, renderTree(n, {renderNoComments})]) case n.kind of nkHiddenAddr, nkAddr: return n of nkHiddenDeref, nkDerefExpr: return n.sons[0] of nkBracketExpr: if len(n) == 1: return n.sons[0] else: discard - var valid = isAssignable(c, n) + let valid = isAssignable(c, n) if valid != arLValue: if valid == arLocalLValue: localError(n.info, errXStackEscape, renderTree(n, {renderNoComments})) @@ -2172,7 +2214,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! result = semSym(c, n, n.sym, flags) - of nkEmpty, nkNone, nkCommentStmt: + of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: if result.typ == nil: result.typ = getSysType(tyNil) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index acea9330b..32b385308 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -174,6 +174,8 @@ proc sideEffectsCheck(c: PContext, s: PSym) = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = + internalAssert header.kind == tyGenericInvocation + var typeMap: LayeredIdTable cl: TReplTypeVars @@ -185,7 +187,35 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes + + # We must add all generic params in scope, because the generic body + # may include tyFromExpr nodes depending on these generic params. + # XXX: This looks quite similar to the code in matchUserTypeClass, + # perhaps the code can be extracted in a shared function. + openScope(c) + let genericTyp = header.base + for i in 0 .. (genericTyp.len - 2): + let genParam = genericTyp[i] + var param: PSym + + template paramSym(kind): untyped = + newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info) + + if genParam.kind == tyStatic: + param = paramSym skConst + param.ast = header[i+1].n + param.typ = header[i+1] + else: + param = paramSym skType + param.typ = makeTypeDesc(c, header[i+1]) + + # this scope was not created by the user, + # unused params shoudn't be reported. + param.flags.incl sfUsed + addDecl(c, param) + result = replaceTypeVarsT(cl, header) + closeScope(c) proc instantiateProcType(c: PContext, pt: TIdTable, prc: PSym, info: TLineInfo) = @@ -316,7 +346,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if c.inGenericContext == 0: instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) - paramsTypeCheck(c, result.typ) + if result.magic != mSlice: + # 'toOpenArray' is special and it is allowed to return 'openArray': + paramsTypeCheck(c, result.typ) else: result = oldPrc popProcCon(c) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 63a4eb99a..e2296a0d2 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -76,7 +76,7 @@ proc semConstrField(c: PContext, flags: TExprFlags, return initValue proc caseBranchMatchesExpr(branch, matched: PNode): bool = - for i in 0 .. (branch.len - 2): + for i in 0 .. branch.len-2: if exprStructuralEquivalent(branch[i], matched): return true diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d097a373f..b43a96a87 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -720,7 +720,8 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: - localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) + localError(n.sons[1].info, "inheritance only works with non-final objects; " & + "to enable inheritance write '" & typeToString(realBase) & " of RootObj'") base = nil realBase = nil if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") @@ -1396,7 +1397,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = fixupTypeOf(c, prev, typExpr) result = typExpr.typ else: - result = semTypeExpr(c, n, prev) + if c.inGenericContext > 0 and n.kind == nkCall: + result = makeTypeFromExpr(c, n.copyTree) + else: + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d9b368b0e..09434c925 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -460,6 +460,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = of tyFromExpr: if cl.allowMetaTypes: return + # This assert is triggered when a tyFromExpr was created in a cyclic + # way. You should break the cycle at the point of creation by introducing + # a call such as: `n.typ = makeTypeFromExpr(c, n.copyTree)` + # Otherwise, the cycle will be fatal for the prepareNode call below assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 5d6b5978d..0fea3bbe3 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -155,7 +155,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = else: c.hashSym(t.sym) return - of tyAlias, tyGenericInst, tyUserTypeClasses: + of tyAlias, tySink, tyGenericInst, tyUserTypeClasses: c.hashType t.lastSon, flags return else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e4509ff91..96d815df7 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1145,7 +1145,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: fRange = prev let ff = f.sons[1].skipTypes({tyTypeDesc}) - let aa = a.sons[1].skipTypes({tyTypeDesc}) + # This typeDesc rule is wrong, see bug #7331 + let aa = a.sons[1] #.skipTypes({tyTypeDesc}) if f.sons[0].kind != tyGenericParam and aa.kind == tyEmpty: result = isGeneric @@ -1883,7 +1884,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, return arg elif f.kind == tyTypeDesc: return arg - elif f.kind == tyStatic: + elif f.kind == tyStatic and arg.typ.n != nil: return arg.typ.n else: return argSemantized # argOrig diff --git a/compiler/types.nim b/compiler/types.nim index 7ae02486e..ff3572517 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1136,6 +1136,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyTypeClasses: if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: discard + elif t.isResolvedUserTypeClass: + result = typeAllowedAux(marker, t.lastSon, kind, flags) elif kind notin {skParam, skResult}: result = t of tyGenericBody, tyGenericParam, tyGenericInvocation, diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 5395d4bad..dd4bc5060 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -16,7 +16,7 @@ const byteExcess* = 128 # we use excess-K for immediates wordExcess* = 32768 - MaxLoopIterations* = 1500_000 # max iterations of all loops + MaxLoopIterations* = 1_000_000_000 # max iterations of all loops type diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index e458cad03..76d91d4e7 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -52,7 +52,7 @@ type wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, - wFloatchecks, wNanChecks, wInfChecks, + wFloatchecks, wNanChecks, wInfChecks, wMoveChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, @@ -139,7 +139,7 @@ const "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "noconv", "on", "off", "checks", "rangechecks", "boundchecks", "overflowchecks", "nilchecks", - "floatchecks", "nanchecks", "infchecks", + "floatchecks", "nanchecks", "infchecks", "movechecks", "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 8e47264ac..11d9987d8 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -229,7 +229,7 @@ body { line-height: 20px; color: #444; letter-spacing: 0.15px; - background-color: rgba(252, 248, 244, 0.45); } + background-color: #FDFBFA; } /* Skeleton grid */ .container { diff --git a/doc/lib.rst b/doc/lib.rst index 2e963451d..b46d957a8 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -216,12 +216,6 @@ Math libraries Floating-point environment. Handling of floating-point rounding and exceptions (overflow, zero-devide, etc.). -* `basic2d <basic2d.html>`_ - Basic 2d support with vectors, points, matrices and some basic utilities. - -* `basic3d <basic3d.html>`_ - Basic 3d support with vectors, points, matrices and some basic utilities. - * `mersenne <mersenne.html>`_ Mersenne twister random number generator. @@ -568,29 +562,4 @@ Nimble is a package manager for the Nim programming language. For instructions on how to install Nimble packages see `its README <https://github.com/nim-lang/nimble#readme>`_. -Official packages ------------------ - -These packages are officially supported and will therefore be continually -maintained to ensure that they work with the latest versions of the Nim -compiler. - -.. raw:: html - - <div id="officialPkgList"><b>If you are reading this you are missing - nimblepkglist.js or have javascript disabled in your browser.</b></div> - -Unofficial packages -------------------- - -These packages have been developed by independent Nim developers and as -such may not always be up to date with the latest developments in the -Nim programming language. - -.. raw:: html - - <div id="unofficialPkgList"><b>If you are reading this you are missing - nimblepkglist.js or have javascript disabled in your browser.</b></div> - - <script type="text/javascript" src="nimblepkglist.js"></script> - <script type="text/javascript" src="https://irclogs.nim-lang.org/packages?callback=gotPackageList" async></script> +To see a list of Nimble's packages, check out `https://nimble.directory/ <https://nimble.directory/>`_ or the `packages repos <https://github.com/nim-lang/packages>`_ on GitHub. diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt index 915e87971..0328ffd55 100644 --- a/doc/manual/lexing.txt +++ b/doc/manual/lexing.txt @@ -344,9 +344,11 @@ prefix), binary (prefix ``0b``), octal (prefix ``0o`` or ``0c``) and hexadecimal There exists a literal for each numerical type that is defined. The suffix starting with an apostrophe ('\'') is called a -`type suffix`:idx:. Literals without a type suffix are of the type ``int``, +`type suffix`:idx:. Literals without a type suffix are of an integer type, unless the literal contains a dot or ``E|e`` in which case it is of -type ``float``. For notational convenience the apostrophe of a type suffix +type ``float``. This integer type is ``int`` if the literal is in the range +``low(i32)..high(i32)``, otherwise it is ``int64``. +For notational convenience the apostrophe of a type suffix is optional if it is not ambiguous (only hexadecimal floating point literals with a type suffix can be ambiguous). diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 721b5cff8..14d272cee 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -145,7 +145,7 @@ the variable has been initialized and does not rely on syntactic properties: x = a() else: x = a() - use x + # use x let statement @@ -296,7 +296,7 @@ empty ``discard`` statement should be used. For non ordinal types it is not possible to list every possible value and so these always require an ``else`` part. -As case statements perform compile-time exhaustiveness checks, the value in +As case statements perform compile-time exhaustiveness checks, the value in every ``of`` branch must be known at compile time. This fact is also exploited to generate more performant code. diff --git a/doc/nimc.rst b/doc/nimc.rst index e949df69c..151510df2 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -115,7 +115,7 @@ The ``nim`` executable processes configuration files in the following directories (in this order; later files overwrite previous settings): 1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. -2) ``/home/$user/.config/nim.cfg`` (UNIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. +2) ``$HOME/.config/nim.cfg`` (POSIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. 3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. 4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. 5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ed9c304fe..d09f5f933 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -678,7 +678,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone} + NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = @@ -723,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.escape()) of nnkIdent: res.add(($n.ident).escape()) of nnkSym: res.add(($n.symbol).escape()) of nnkNone: assert false diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 55692d47d..cd7609729 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -45,6 +45,7 @@ type location*: Location closed*: bool defaultStatus*: cstring + devicePixelRatio*: float innerHeight*, innerWidth*: int locationbar*: ref TLocationBar menubar*: ref TMenuBar @@ -53,11 +54,14 @@ type pageXOffset*, pageYOffset*: int personalbar*: ref TPersonalBar scrollbars*: ref TScrollBars + scrollX*: float + scrollY*: float statusbar*: ref TStatusBar status*: cstring toolbar*: ref TToolBar frames*: seq[TFrame] screen*: Screen + performance*: Performance Frame* = ref FrameObj FrameObj {.importc.} = object of WindowObj @@ -253,6 +257,8 @@ type minHeight*: cstring minWidth*: cstring overflow*: cstring + overflowX*: cstring + overflowY*: cstring padding*: cstring paddingBottom*: cstring paddingLeft*: cstring @@ -400,6 +406,40 @@ type once*: bool passive*: bool + BoundingRect* {.importc.} = ref object + top*, bottom*, left*, right*, x*, y*, width*, height*: float + + PerformanceMemory* {.importc.} = ref object + jsHeapSizeLimit*: float + totalJSHeapSize*: float + usedJSHeapSize*: float + + PerformanceTiming* {.importc.} = ref object + connectStart*: float + domComplete*: float + domContentLoadedEventEnd*: float + domContentLoadedEventStart*: float + domInteractive*: float + domLoading*: float + domainLookupEnd*: float + domainLookupStart*: float + fetchStart*: float + loadEventEnd*: float + loadEventStart*: float + navigationStart*: float + redirectEnd*: float + redirectStart*: float + requestStart*: float + responseEnd*: float + responseStart*: float + secureConnectionStart*: float + unloadEventEnd*: float + unloadEventStart*: float + + Performance* {.importc.} = ref object + memory*: PerformanceMemory + timing*: PerformanceTiming + {.push importcpp.} # EventTarget "methods" @@ -451,6 +491,7 @@ proc cloneNode*(n: Node, copyContent: bool): Node proc deleteData*(n: Node, start, len: int) proc getAttribute*(n: Node, attr: cstring): cstring proc getAttributeNode*(n: Node, attr: cstring): Node +proc getBoundingClientRect*(e: Node): BoundingRect proc hasChildNodes*(n: Node): bool proc insertBefore*(n, newNode, before: Node) proc insertData*(n: Node, position: int, data: cstring) @@ -530,6 +571,9 @@ proc preventDefault*(ev: Event) proc identifiedTouch*(list: TouchList): Touch proc item*(list: TouchList, i: int): Touch +# Performance "methods" +proc now*(p: Performance): float + {.pop.} var diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 23a0cdb02..2f7b44b31 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -84,7 +84,7 @@ proc smartBinarySearch*[T](a: openArray[T], key: T): int = const onlySafeCode = true -proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = +proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int = ## same as binarySearch except that if key is not in `a` then this ## returns the location where `key` would be if it were. In other ## words if you have a sorted sequence and you call diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 97bec2815..1df7c3fc0 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -78,7 +78,10 @@ proc getFileSize*(f: AsyncFile): int64 = raiseOSError(osLastError()) result = (high shl 32) or low else: + let curPos = lseek(f.fd.cint, 0, SEEK_CUR) result = lseek(f.fd.cint, 0, SEEK_END) + f.offset = lseek(f.fd.cint, curPos, SEEK_SET) + assert(f.offset == curPos) proc newAsyncFile*(fd: AsyncFd): AsyncFile = ## Creates `AsyncFile` with a previously opened file descriptor `fd`. @@ -281,6 +284,7 @@ proc read*(f: AsyncFile, size: int): Future[string] = result = false # We still want this callback to be called. elif res == 0: # EOF + f.offset = lseek(fd.cint, 0, SEEK_CUR) retFuture.complete("") else: readBuffer.setLen(res) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index c97846f92..7b508b294 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -158,6 +158,12 @@ template getOrDefaultImpl(t, key): untyped = var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val +template getOrDefaultImpl(t, key, default: untyped): untyped = + mixin rawGet + var hc: Hash + var index = rawGet(t, key, hc) + result = if index >= 0: t.data[index].val else: default + proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} = ## retrieves the value at ``t[key]``. If `key` is not in `t`, the ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether @@ -175,10 +181,18 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} = ## instead. get(t, key) -proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key) +proc getOrDefault*[A, B](t: Table[A, B], key: A): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any + ## integer type). + getOrDefaultImpl(t, key) -template withValue*[A, B](t: var Table[A, B], key: A, - value, body: untyped) = +proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default` + ## is returned. + getOrDefaultImpl(t, key, default) + +template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = ## retrieves the value at ``t[key]``. ## `value` can be modified in the scope of the ``withValue`` call. ## @@ -325,8 +339,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] = result.counter = 0 newSeq(result.data, initialSize) -proc toTable*[A, B](pairs: openArray[(A, - B)]): Table[A, B] = +proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = ## creates a new hash table that contains the given `pairs`. result = initTable[A, B](rightSize(pairs.len)) for key, val in items(pairs): result[key] = val @@ -410,7 +423,16 @@ proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} = ## Use ```[]``` instead. t[][key] -proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key) +proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any + ## integer type). + getOrDefault(t[], key) + +proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default` + ## is returned. + getOrDefault(t[], key, default) proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way @@ -562,8 +584,15 @@ proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} = get(t, key) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any + ## integer type). getOrDefaultImpl(t, key) +proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default` + ## is returned. + getOrDefaultImpl(t, key, default) proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. @@ -630,8 +659,7 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] = result.last = -1 newSeq(result.data, initialSize) -proc toOrderedTable*[A, B](pairs: openArray[(A, - B)]): OrderedTable[A, B] = +proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = ## creates a new ordered hash table that contains the given `pairs`. result = initOrderedTable[A, B](rightSize(pairs.len)) for key, val in items(pairs): result[key] = val @@ -657,8 +685,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = hs = nxts return true -proc sort*[A, B](t: var OrderedTable[A, B], - cmp: proc (x,y: (A, B)): int) = +proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) = ## sorts `t` according to `cmp`. This modifies the internal list ## that kept the insertion order, so insertion order is lost after this ## call but key lookup and insertions remain possible after `sort` (in @@ -748,8 +775,16 @@ proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} = result = t[][key] proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any + ## integer type). getOrDefault(t[], key) +proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default` + ## is returned. + getOrDefault(t[], key, default) + proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. @@ -802,8 +837,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = elif isNil(t): result = false else: result = s[] == t[] -proc sort*[A, B](t: OrderedTableRef[A, B], - cmp: proc (x,y: (A, B)): int) = +proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) = ## sorts `t` according to `cmp`. This modifies the internal list ## that kept the insertion order, so insertion order is lost after this ## call but key lookup and insertions remain possible after `sort` (in @@ -916,9 +950,17 @@ proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} = ctget(t, key) proc getOrDefault*[A](t: CountTable[A], key: A): int = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, 0 (the + ## default initialization value of `int`), is returned. var index = rawGet(t, key) if index >= 0: result = t.data[index].val +proc getOrDefault*[A](t: CountTable[A], key: A, default: int): int = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## integer value of `default` is returned. + var index = rawGet(t, key) + result = if index >= 0: t.data[index].val else: default + proc hasKey*[A](t: CountTable[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 @@ -1073,8 +1115,15 @@ proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} = result = t[][key] proc getOrDefault*[A](t: CountTableRef[A], key: A): int = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, 0 (the + ## default initialization value of `int`), is returned. result = t[].getOrDefault(key) +proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = + ## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the + ## integer value of `default` is returned. + result = t[].getOrDefault(key, default) + proc hasKey*[A](t: CountTableRef[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) @@ -1267,7 +1316,7 @@ when isMainModule: #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode doAssert 0 == t.getOrDefault(testKey) - t.inc(testKey,3) + t.inc(testKey, 3) doAssert 3 == t.getOrDefault(testKey) block: @@ -1334,3 +1383,21 @@ when isMainModule: block: # CountTable.smallest let t = toCountTable([0, 0, 5, 5, 5]) doAssert t.smallest == (0, 2) + + block: + var tp: Table[string, string] = initTable[string, string]() + doAssert "test1" == tp.getOrDefault("test1", "test1") + tp["test2"] = "test2" + doAssert "test2" == tp.getOrDefault("test2", "test1") + var tr: TableRef[string, string] = newTable[string, string]() + doAssert "test1" == tr.getOrDefault("test1", "test1") + tr["test2"] = "test2" + doAssert "test2" == tr.getOrDefault("test2", "test1") + var op: OrderedTable[string, string] = initOrderedTable[string, string]() + doAssert "test1" == op.getOrDefault("test1", "test1") + op["test2"] = "test2" + doAssert "test2" == op.getOrDefault("test2", "test1") + var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]() + doAssert "test1" == orf.getOrDefault("test1", "test1") + orf["test2"] = "test2" + doAssert "test2" == orf.getOrDefault("test2", "test1") diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index fda41dadb..ec6cbb232 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -10,6 +10,49 @@ ## This module implements the ability to access symbols from shared ## libraries. On POSIX this uses the ``dlsym`` mechanism, on ## Windows ``LoadLibrary``. +## +## Examples +## -------- +## +## Loading a simple C function +## ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +## +## The following example demonstrates loading a function called 'greet' +## from a library that is determined at runtime based upon a language choice. +## If the library fails to load or the function 'greet' is not found, +## it quits with a failure error code. +## +## .. code-block::nim +## +## import dynlib +## +## type +## greetFunction = proc(): cstring {.gcsafe, stdcall.} +## +## let lang = stdin.readLine() +## +## let lib = case lang +## of "french": +## loadLib("french.dll") +## else: +## loadLib("english.dll") +## +## if lib == nil: +## echo "Error loading library" +## quit(QuitFailure) +## +## let greet = cast[greetFunction](lib.symAddr("greet")) +## +## if greet == nil: +## echo "Error loading 'greet' function from library" +## quit(QuitFailure) +## +## let greeting = greet() +## +## echo greeting +## +## unloadLib(lib) +## import strutils diff --git a/lib/pure/json.nim b/lib/pure/json.nim index bbde4db5f..912cd6837 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -734,7 +734,7 @@ proc getFields*(n: JsonNode, else: return n.fields proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = - ## Retrieves the int value of a `JArray JsonNode`. + ## Retrieves the array of a `JArray JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil. if n.isNil or n.kind != JArray: return default @@ -964,6 +964,16 @@ proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = return nil result = result.fields.getOrDefault(key) +proc `{}`*(node: JsonNode, index: varargs[int]): JsonNode = + ## Traverses the node and gets the given value. If any of the + ## indexes do not exist, returns ``nil``. Also returns ``nil`` if one of the + ## intermediate data structures is not an array. + result = node + for i in index: + if isNil(result) or result.kind != JArray or i >= node.len: + return nil + result = result.elems[i] + proc getOrDefault*(node: JsonNode, key: string): JsonNode = ## Gets a field from a `node`. If `node` is nil or not an object or ## value at `key` does not exist, returns nil diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 336068df0..ccf02a1fc 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1701,6 +1701,9 @@ proc connect*(socket: Socket, address: string, port = Port(0), if selectWrite(s, timeout) != 1: raise newException(TimeoutError, "Call to 'connect' timed out.") else: + let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR) + if res != 0: + raiseOSError(OSErrorCode(res)) when defineSsl and not defined(nimdoc): if socket.isSSL: socket.fd.setBlocking(true) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 6c1e1fe87..75b5bac43 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -274,6 +274,18 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = # so we need to compensate for that here. result.inc dt.utcOffset +proc `<`*(a, b: DateTime): bool = + ## Returns true iff ``a < b``, that is iff a happened before b. + return a.toTime < b.toTime + +proc `<=` * (a, b: DateTime): bool = + ## Returns true iff ``a <= b``. + return a.toTime <= b.toTime + +proc `==`*(a, b: DateTime): bool = + ## Returns true if ``a == b``, that is if both dates represent the same point in datetime. + return a.toTime == b.toTime + proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = let adjTime = zt.adjTime.int64 let epochday = (if adjTime >= 0: adjTime else: adjTime - (secondsInDay - 1)) div secondsInDay diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index fbce087ff..0034d0c60 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -150,6 +150,7 @@ var checkpoints {.threadvar.}: seq[string] formatters {.threadvar.}: seq[OutputFormatter] testsFilters {.threadvar.}: HashSet[string] + disabledParamFiltering {.threadvar.}: bool when declared(stdout): abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") @@ -379,7 +380,7 @@ proc ensureInitialized() = if formatters == nil: formatters = @[OutputFormatter(defaultConsoleFormatter())] - if not testsFilters.isValid: + if not disabledParamFiltering and not testsFilters.isValid: testsFilters.init() when declared(paramCount): # Read tests to run from the command line. @@ -701,3 +702,7 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = errorTypes.add(exp[i]) result = getAst(expectBody(errorTypes, exp.lineinfo, body)) + +proc disableParamFiltering* = + ## disables filtering tests with the command line params + disabledParamFiltering = true diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 22bd259b7..9159b4bfc 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -12,9 +12,9 @@ import streams, parsexml, strtabs, xmltree type - XmlError* = object of ValueError ## exception that is raised - ## for invalid XML - errors*: seq[string] ## all detected parsing errors + XmlError* = object of ValueError ## Exception that is raised + ## for invalid XML. + errors*: seq[string] ## All detected parsing errors. {.deprecated: [EInvalidXml: XmlError].} @@ -102,8 +102,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = proc parseXml*(s: Stream, filename: string, errors: var seq[string]): XmlNode = - ## parses the XML from stream `s` and returns a ``PXmlNode``. Every - ## occurred parsing error is added to the `errors` sequence. + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. Every + ## occurred parsing error is added to the ``errors`` sequence. var x: XmlParser open(x, s, filename, {reportComments}) while true: @@ -121,15 +121,20 @@ proc parseXml*(s: Stream, filename: string, close(x) proc parseXml*(s: Stream): XmlNode = - ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing - ## errors are turned into an ``EInvalidXML`` exception. + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. var errors: seq[string] = @[] - result = parseXml(s, "unknown_html_doc", errors) + result = parseXml(s, "unknown_xml_doc", errors) if errors.len > 0: raiseInvalidXml(errors) +proc parseXml*(str: string): XmlNode = + ## Parses the XML from string ``str`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. + parseXml(newStringStream(str)) + proc loadXml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. Every occurred parsing error is added to the `errors` + ## a ``XmlNode``. Every occurred parsing error is added to the ``errors`` ## sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) @@ -137,7 +142,7 @@ proc loadXml*(path: string, errors: var seq[string]): XmlNode = proc loadXml*(path: string): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` + ## a ``XmlNode``. All parsing errors are turned into an ``XmlError`` ## exception. var errors: seq[string] = @[] result = loadXml(path, errors) diff --git a/lib/system.nim b/lib/system.nim index b71a3c73c..7eda30276 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -617,6 +617,11 @@ type ## ## This is only raised if the ``segfaults.nim`` module was imported! +when defined(nimNewRuntime): + type + MoveError* = object of SystemError ## \ + ## Raised on attempts to re-sink an already consumed ``sink`` parameter. + {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, FWriteIO: WriteIOEffect, FExecIO: ExecIOEffect, @@ -1774,7 +1779,7 @@ when not defined(nimscript): proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or - ## ``free(block)``. The block is not initialized, so reading + ## ``dealloc(block)``. The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `createSharedU` to allocate from a shared heap. @@ -1789,7 +1794,7 @@ when not defined(nimscript): proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or - ## ``free(block)``. The block is initialized with all bytes + ## ``dealloc(block)``. The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``createU``. ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. @@ -1807,7 +1812,7 @@ when not defined(nimscript): ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not - ## **nil** ``resize`` calls ``free(p)``. In other cases the block + ## **nil** ``resize`` calls ``dealloc(p)``. In other cases the block ## has to be freed with ``free``. The allocated memory belongs to ## its allocating thread! ## Use `resizeShared` to reallocate from a shared heap. @@ -4131,3 +4136,13 @@ when defined(cpp) and appType != "lib" and not defined(js) and stderr.write trace & "Error: unhandled exception: " & ex.msg & " [" & $ex.name & "]\n" quit 1 + +when not defined(js): + proc toOpenArray*[T](x: seq[T]; first, last: int): openarray[T] {. + magic: "Slice".} + proc toOpenArray*[T](x: openarray[T]; first, last: int): openarray[T] {. + magic: "Slice".} + proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openarray[T] {. + magic: "Slice".} + proc toOpenArray*(x: string; first, last: int): openarray[char] {. + magic: "Slice".} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 4291013a2..6aef4f411 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -29,7 +29,9 @@ const FliOffset = 6 RealFli = MaxFli - FliOffset - HugeChunkSize = int high(int32) - 1 # 2 GB, depends on TLSF's impl + # size of chunks in last matrix bin + MaxBigChunkSize = 1 shl MaxFli - 1 shl (MaxFli-MaxLog2Sli-1) + HugeChunkSize = MaxBigChunkSize + 1 type PTrunk = ptr Trunk @@ -154,10 +156,11 @@ proc mappingSearch(r, fl, sl: var int) {.inline.} = # PageSize alignment: let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1 r = r + t + r = r and not t + r = min(r, MaxBigChunkSize) fl = msbit(uint32 r) sl = (r shr (fl - MaxLog2Sli)) - MaxSli dec fl, FliOffset - r = r and not t sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned") # See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of @@ -518,55 +521,61 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk, if isAccessible(a, ri): ri.prevSize = prevSize or (ri.prevSize and 1) +proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk = + result = cast[PBigChunk](cast[ByteAddress](c) +% size) + result.size = c.size - size + track("result.origSize", addr result.origSize, sizeof(int)) + # XXX check if these two nil assignments are dead code given + # addChunkToMatrix's implementation: + result.next = nil + result.prev = nil + # size and not used: + result.prevSize = size + sysAssert((size and 1) == 0, "splitChunk 2") + sysAssert((size and PageMask) == 0, + "splitChunk: size is not a multiple of the PageSize") + updatePrevSize(a, c, result.size) + c.size = size + incl(a, a.chunkStarts, pageIndex(result)) + +proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) = + let rest = splitChunk2(a, c, size) + addChunkToMatrix(a, rest) + proc freeBigChunk(a: var MemRegion, c: PBigChunk) = var c = c sysAssert(c.size >= PageSize, "freeBigChunk") inc(a.freeMem, c.size) - when coalescRight: - var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) - sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") - if isAccessible(a, ri) and chunkUnused(ri): - sysAssert(not isSmallChunk(ri), "freeBigChunk 3") - if not isSmallChunk(ri): - removeChunkFromMatrix(a, cast[PBigChunk](ri)) - inc(c.size, ri.size) - excl(a.chunkStarts, pageIndex(ri)) + c.prevSize = c.prevSize and not 1 # set 'used' to false when coalescLeft: - let prevSize = c.prevSize and not 1 + let prevSize = c.prevSize if prevSize != 0: var le = cast[PChunk](cast[ByteAddress](c) -% prevSize) sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") - if not isSmallChunk(le): + if not isSmallChunk(le) and le.size < MaxBigChunkSize: removeChunkFromMatrix(a, cast[PBigChunk](le)) inc(le.size, c.size) excl(a.chunkStarts, pageIndex(c)) c = cast[PBigChunk](le) - - incl(a, a.chunkStarts, pageIndex(c)) - updatePrevSize(a, c, c.size) + if c.size > MaxBigChunkSize: + let rest = splitChunk2(a, c, MaxBigChunkSize) + addChunkToMatrix(a, c) + c = rest + when coalescRight: + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") + if isAccessible(a, ri) and chunkUnused(ri): + sysAssert(not isSmallChunk(ri), "freeBigChunk 3") + if not isSmallChunk(ri) and c.size < MaxBigChunkSize: + removeChunkFromMatrix(a, cast[PBigChunk](ri)) + inc(c.size, ri.size) + excl(a.chunkStarts, pageIndex(ri)) + if c.size > MaxBigChunkSize: + let rest = splitChunk2(a, c, MaxBigChunkSize) + addChunkToMatrix(a, rest) addChunkToMatrix(a, c) - # set 'used' to false: - c.prevSize = c.prevSize and not 1 - -proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) = - var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) - rest.size = c.size - size - track("rest.origSize", addr rest.origSize, sizeof(int)) - # XXX check if these two nil assignments are dead code given - # addChunkToMatrix's implementation: - rest.next = nil - rest.prev = nil - # size and not used: - rest.prevSize = size - sysAssert((size and 1) == 0, "splitChunk 2") - sysAssert((size and PageMask) == 0, - "splitChunk: size is not a multiple of the PageSize") - updatePrevSize(a, c, rest.size) - c.size = size - incl(a, a.chunkStarts, pageIndex(rest)) - addChunkToMatrix(a, rest) proc getBigChunk(a: var MemRegion, size: int): PBigChunk = sysAssert(size > 0, "getBigChunk 2") diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 69b680dbd..d3651f659 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -52,6 +52,11 @@ proc chckNil(p: pointer) = if p == nil: sysFatal(NilAccessError, "attempt to write to a nil address") +when defined(nimNewRuntime): + proc chckMove(b: bool) {.compilerproc.} = + if not b: + sysFatal(MoveError, "attempt to access an object that was moved") + proc chckNilDisp(p: pointer) {.compilerproc.} = if p == nil: sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index e53d5308a..4464eae50 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -13,10 +13,10 @@ when defined(Unix): when defined(macosx): const - lib = "(libmysqlclient|libmariadbclient).(|20|19|18|17|16|15).dylib" + lib = "(libmysqlclient|libmariadbclient)(|.20|.19|.18|.17|.16|.15).dylib" else: const - lib = "(libmysqlclient|libmariadbclient).so.(|20|19|18|17|16|15)" + lib = "(libmysqlclient|libmariadbclient).so(|.20|.19|.18|.17|.16|.15)" when defined(Windows): const lib = "(libmysql.dll|libmariadb.dll)" diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index eefc09cb9..357343bff 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -38,7 +38,7 @@ when useWinVersion: from winlean import SocketHandle else: - const versions = "(.1.1|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" + const versions = "(.1.1|.38|.39|.41|.43|.44|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" when defined(macosx): const diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 0328b817a..abfb21c13 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -608,9 +608,9 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) = options.command = "nimsuggest" let scriptFile = gProjectFull.changeFileExt("nims") if fileExists(scriptFile): - runNimScript(cache, scriptFile, freshDefines=false, config) - # 'nim foo.nims' means to just run the NimScript file and do nothing more: - if scriptFile == gProjectFull: return + # 'nimsuggest foo.nims' means to just auto-complete the NimScript file: + if scriptFile != gProjectFull: + runNimScript(cache, scriptFile, freshDefines=false, config) elif fileExists(gProjectPath / "config.nims"): # directory wide NimScript file runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config) diff --git a/readme.md b/readme.md index ebcf5f802..44c92d06b 100644 --- a/readme.md +++ b/readme.md @@ -52,6 +52,10 @@ Nim from source using ``gcc``, ``git`` and the ``koch`` build tool (in the place of ``sh build.sh`` you should substitute ``build.bat`` on x86 Windows or ``build64.bat`` on x86_64 Windows): +**Note: The following commands are for the development version of the compiler.** +For most users, installing the latest stable version is enough. Check out +the installation instructions on the website to do so: https://nim-lang.org/install.html. + ``` git clone https://github.com/nim-lang/Nim.git cd Nim @@ -61,6 +65,7 @@ sh build.sh cd ../ bin/nim c koch ./koch boot -d:release +./koch tools # Compile Nimble and other tools. ``` Finally, once you have finished the build steps (on Windows, Mac or Linux) you @@ -80,11 +85,9 @@ For more information on the ``koch`` build tool please see the documentation within the [doc/koch.rst](doc/koch.rst) file. ## Nimble -``nimble`` is Nim's package manager and it can be acquired from the -[``nim-lang/nimble``][nimble-repo] repository. Assuming that you added Nim's -``bin`` directory to your PATH, you may install Nimble from source by running -``koch nimble`` within the root of the cloned repository. +``nimble`` is Nim's package manager. To learn more about it, see the +[``nim-lang/nimble``][nimble-repo] repository. ## Contributors diff --git a/tests/async/hello.txt b/tests/async/hello.txt new file mode 100644 index 000000000..854d6c20a --- /dev/null +++ b/tests/async/hello.txt @@ -0,0 +1 @@ +hello humans! \ No newline at end of file diff --git a/tests/async/tasyncfile.nim b/tests/async/tasyncfile.nim index aa7f03ab1..c7b71a2f7 100644 --- a/tests/async/tasyncfile.nim +++ b/tests/async/tasyncfile.nim @@ -1,4 +1,8 @@ discard """ + output: '''13 +hello humans! +13 +''' file: "tasyncfile.nim" exitcode: 0 """ @@ -48,5 +52,12 @@ proc main() {.async.} = doAssert data == "t3" file.close() + # Issue #7347 + block: + let appDir = getAppDir() + var file = openAsync(appDir & DirSep & "hello.txt") + echo file.getFileSize() + echo await file.readAll() + echo file.getFilePos() waitFor main() diff --git a/tests/ccgbugs/mymodule.nim b/tests/ccgbugs/mymodule.nim new file mode 100644 index 000000000..8c78cdf9b --- /dev/null +++ b/tests/ccgbugs/mymodule.nim @@ -0,0 +1,14 @@ +type + MyRefObject* = ref object + s: string + + BaseObj* = ref object of RootObj + ChildObj* = ref object of BaseObj + +proc newMyRefObject*(s: string): MyRefObject = + new(result) + result.s = s + +proc `$`*(o: MyRefObject): string = + o.s + \ No newline at end of file diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim new file mode 100644 index 000000000..2a867bc3b --- /dev/null +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -0,0 +1,34 @@ +discard """ +ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" +output: "hello" +""" + +# issue #7339 +# Test that MyRefObject is only forward declared as it used only by reference + +import mymodule +type AnotherType = object + f: MyRefObject + +let x = AnotherType(f: newMyRefObject("hello")) +echo $x.f + + +# bug #7363 + +type + Foo = object + a: cint + Foo2 = object + b: cint + +proc f(foo: ptr Foo, foo2: ptr Foo2): cint = + if foo != nil: {.emit: "`result` = `foo`->a;".} + if foo2 != nil: {.emit: [result, " = ", foo2[], ".b;"].} + +discard f(nil, nil) + + +# bug #7392 +var x1: BaseObj +var x2 = ChildObj(x1) diff --git a/tests/ccgbugs/tresult_of_array.nim b/tests/ccgbugs/tresult_of_array.nim new file mode 100644 index 000000000..fb5abf18a --- /dev/null +++ b/tests/ccgbugs/tresult_of_array.nim @@ -0,0 +1,29 @@ +discard """ + output: '''false +true +false +[false, false, false] +''' +""" + +# bug #7332 +# resetLoc generate incorrect memset code +# because of array passed as argument decaying into a pointer + +import tables +const tableOfArray = { + "one": [true, false, false], + "two": [false, true, false], + "three": [false, false, true] +}.toTable() +for i in 0..2: + echo tableOfArray["two"][i] + +var seqOfArray = @[ + [true, false, false], + [false, true, false], + [false, false, true] +] +proc crashingProc*[B](t: seq[B], index: Natural): B = + discard +echo seqOfArray.crashingProc(0) diff --git a/tests/concepts/tseqofconcept.nim b/tests/concepts/tseqofconcept.nim new file mode 100644 index 000000000..5e44117ea --- /dev/null +++ b/tests/concepts/tseqofconcept.nim @@ -0,0 +1,19 @@ +discard """ +output: "1\n2\n3" +""" + +type + MyConcept = concept x + someProc(x) + + SomeSeq = seq[MyConcept] + +proc someProc(x:int) = echo x + +proc work (s: SomeSeq) = + for item in s: + someProc item + +var s = @[1, 2, 3] +work s + diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index 9365a3a18..f81d67a50 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -300,7 +300,7 @@ reject wantsVarPointer2(pcat) # covariance may be allowed for certain extern types -{.emit: """ +{.emit: """/*TYPESECTION*/ template <class T> struct FN { typedef void (*type)(T); }; template <class T> struct ARR { typedef T DataType[2]; DataType data; }; """.} diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim index 9df3754ba..4d686955f 100644 --- a/tests/cpp/tvector_iterator.nim +++ b/tests/cpp/tvector_iterator.nim @@ -2,7 +2,7 @@ discard """ targets: "cpp" """ -{.emit: """ +{.emit: """/*TYPESECTION*/ template <class T> struct Vector { diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim new file mode 100644 index 000000000..b6b3d101f --- /dev/null +++ b/tests/errmsgs/twrong_at_operator.nim @@ -0,0 +1,15 @@ +discard """ +errormsg: "type mismatch: got <array[0..0, type int]>" +line: 15 +nimout: ''' +twrong_at_operator.nim(15, 30) Error: type mismatch: got <array[0..0, type int]> +but expected one of: +proc `@`[T](a: openArray[T]): seq[T] +proc `@`[IDX, T](a: array[IDX, T]): seq[T] + +expression: @[int] +''' +""" + +# bug #7331 +var seqOfStrings: seq[int] = @[int] diff --git a/tests/exception/tfinally.nim b/tests/exception/tfinally.nim index e5315a318..7a218b444 100644 --- a/tests/exception/tfinally.nim +++ b/tests/exception/tfinally.nim @@ -7,6 +7,11 @@ msg1 msg2 finally2 finally1 +----------- +except1 +finally1 +except2 +finally2 ''' """ # Test return in try statement: @@ -39,4 +44,19 @@ proc nested_finally = finally: echo "finally1" -nested_finally() \ No newline at end of file +nested_finally() + +echo "-----------" +#bug 7414 +try: + try: + raise newException(Exception, "Hello") + except: + echo "except1" + raise + finally: + echo "finally1" +except: + echo "except2" +finally: + echo "finally2" \ No newline at end of file diff --git a/tests/fragmentation/tfragment_gc.nim b/tests/fragmentation/tfragment_gc.nim index c930ec931..d387bbea2 100644 --- a/tests/fragmentation/tfragment_gc.nim +++ b/tests/fragmentation/tfragment_gc.nim @@ -1,6 +1,7 @@ discard """ output: '''occupied ok: true total ok: true''' + disabled: "windows" """ import strutils, data diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim index faed77225..0a1836886 100644 --- a/tests/macros/tdumpastgen.nim +++ b/tests/macros/tdumpastgen.nim @@ -2,16 +2,33 @@ discard """ msg: '''nnkStmtList.newTree( nnkVarSection.newTree( nnkIdentDefs.newTree( - newIdentNode(!"x"), + newIdentNode("x"), newEmptyNode(), nnkCall.newTree( nnkDotExpr.newTree( - newIdentNode(!"foo"), - newIdentNode(!"create") + newIdentNode("baz"), + newIdentNode("create") ), newLit(56) ) ) + ), + nnkProcDef.newTree( + newIdentNode("foo"), + newEmptyNode(), + newEmptyNode(), + nnkFormalParams.newTree( + newEmptyNode() + ), + newEmptyNode(), + newEmptyNode(), + nnkStmtList.newTree( + newCommentStmtNode("This is a docstring"), + nnkCommand.newTree( + newIdentNode("echo"), + newLit("bar") + ) + ) ) )''' """ @@ -21,5 +38,8 @@ msg: '''nnkStmtList.newTree( import macros dumpAstGen: - var x = foo.create(56) + var x = baz.create(56) + proc foo() = + ## This is a docstring + echo "bar" diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index bd3295874..02021185f 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -14,7 +14,6 @@ AST b 20Test 20 ''' - disabled: true """ import macros @@ -26,7 +25,7 @@ type const data: Tconfig = (@["aa", "bb"], @[11, 22]) -macro mymacro(data: static[TConfig]) = +macro mymacro(data: static[TConfig]): untyped = echo "letters" for s in items(data.letters): echo s @@ -44,10 +43,10 @@ const a : Ta = @[(11, 22), (33, 44)] b : Tb = (@[55,66], @[77, 88]) -macro mA(data: static[Ta]) = +macro mA(data: static[Ta]): untyped = echo "AST a \n", repr(data) -macro mB(data: static[Tb]) = +macro mB(data: static[Tb]): untyped = echo "AST b \n", repr(data) echo data.e[0] @@ -57,13 +56,15 @@ mB(b) type Foo[N: static[int], Z: static[string]] = object -macro staticIntMacro(f: static[int]) = echo f +macro staticIntMacro(f: static[int]): untyped = + echo f + staticIntMacro 10 var x: Foo[20, "Test"] -macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12) = +macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): untyped = echo N, Z genericMacro x diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index c29fd15ce..1209fe78f 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -1,4 +1,9 @@ -import macros +discard """ +output: "8\n8\n4" +""" + +import + macros, typetraits template selectType(x: int): typeDesc = when x < 10: @@ -11,6 +16,9 @@ template simpleTypeTempl: typeDesc = macro typeFromMacro: typedesc = string +# The tests below check that the result variable of the +# selected type matches the literal types in the code: + proc t1*(x: int): simpleTypeTempl() = result = "test" @@ -37,3 +45,57 @@ proc t6*(x: type(t3(0))): type(t1(0)) = proc t7*(x: int): type($x) = result = "test" +# This is a more compicated example involving a type +# selection through a macro: +# https://github.com/nim-lang/Nim/issues/7230 + +macro getBase*(bits: static[int]): untyped = + if bits >= 128: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) + else: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) + +type + BaseUint* = SomeUnsignedInt or MpUintBase + + MpUintBase*[T] = object + lo*, hi*: T + + ## This gets type mismatch + MpUint*[bits: static[int]] = getBase(bits) + +var m1: MpUint[128] +var m2: MpUint[64] +var m3: getBase(32) + +static: + # assert m1.type.name == "MpUintBase[uint64]" + assert m1.lo.type.name == "uint64" + assert m2.lo.type.name == "uint32" + assert m3.type.name == "MpUintBase[system.uint32]" + +# https://github.com/nim-lang/Nim/issues/7379 + +import macros, typetraits + +macro works(): untyped = + result = getType(int64) + +macro fails(bits: static[int]): untyped = + if bits > 64: + result = getType(int64) + else: + result = getType(int32) + +type + Foo*[bits: static[int]] = works() + Bar*[bits: static[int]] = fails(bits) + +var a: Foo[16] +var b: Bar[256] +var c: Bar[32] + +echo sizeof(a) +echo sizeof(b) +echo sizeof(c) + diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim index 8b99eb501..c845e04f7 100644 --- a/tests/metatype/typeclassinference.nim +++ b/tests/metatype/typeclassinference.nim @@ -1,7 +1,6 @@ discard """ errormsg: "type mismatch: got <string> but expected 'ptr'" line: 20 - disabled: true """ import typetraits @@ -12,7 +11,7 @@ type var x = Vec([1, 2, 3]) static: - assert x.type.name == "Vec[static[int](3), int]" + assert x.type.name == "Vec[3, system.int]" var str1: string = "hello, world!" var ptr1: ptr = addr(str1) diff --git a/tests/misc/tidentconcatenations.nim b/tests/misc/tidentconcatenations.nim new file mode 100644 index 000000000..302c51d87 --- /dev/null +++ b/tests/misc/tidentconcatenations.nim @@ -0,0 +1,32 @@ +type + Hash*[bits: static[int]] = object + data*: array[bits div 8, uint8] + +{.emit: """ + +void sha_256(void* input, int input_len, void* output, int output_len) {} +void sha_512(void* input, int input_len, void* output, int output_len) {} + +void keccak_256(void* input, int input_len, void* output, int output_len) {} +void keccak_512(void* input, int input_len, void* output, int output_len) {} + +""".} + +template defineKeccak(bits: untyped) = + proc `extKeccak bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "keccak_" & astToStr(bits).} + +template defineSha(bits: static[int]) = + proc `extSha bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "sha_" & astToStr(bits).} + +template defineHashProcs(bits) = + defineSha(bits) + defineKeccak(bits) + +defineHashProcs(256) +defineHashProcs(512) + +extSha256(nil, 0, nil, 0) +extSha512(nil, 0, nil, 0) +extKeccak256(nil, 0, nil, 0) +extKeccak512(nil, 0, nil, 0) + diff --git a/tests/statictypes/tcryptodigest.nim b/tests/statictypes/tcryptodigest.nim new file mode 100644 index 000000000..c78a4188a --- /dev/null +++ b/tests/statictypes/tcryptodigest.nim @@ -0,0 +1,44 @@ +discard """ + output: "Digest[128]\nDigest[256]" +""" + +import typetraits + +type + Digest[bits: static[int]] = object + data: array[bits div 8, byte] + + ContextKind = enum + A, B, C + + HashingContext[bits: static[int], kind: static[ContextKind]] = object + ctx: array[bits div 8, byte] + + Hash128 = HashingContext[128, A] + Hash256 = HashingContext[256, B] + + HMAC[HashType] = object + h: HashType + +proc init(c: var HashingContext) = discard +proc update(c: var HashingContext, data: ptr byte, dataLen: uint) = discard +proc finish(c: var HashingContext): Digest[c.bits] = discard + +proc digest(T: typedesc, data: ptr byte, dataLen: uint): Digest[T.bits] = + mixin init, update, finish + + var ctx: T + ctx.init() + ctx.update(data, dataLen) + result = ctx.finish() + +var h = Hash128.digest(nil, 0) +echo h.type.name + +proc finish(hmac: var HMAC): Digest[HMAC.HashType.bits] = + discard + +var hm: HMAC[Hash256] +var d = hm.finish +echo d.type.name + diff --git a/tests/statictypes/tstaticimportcpp.nim b/tests/statictypes/tstaticimportcpp.nim index e13cc36b4..0cbbc1df6 100644 --- a/tests/statictypes/tstaticimportcpp.nim +++ b/tests/statictypes/tstaticimportcpp.nim @@ -1,9 +1,9 @@ discard """ targets: "cpp" -output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest" +output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest\n[0, 0, 20, 0]" """ -{.emit: """ +{.emit: """/*TYPESECTION*/ template <int N, class T> struct GenericIntType { @@ -51,3 +51,9 @@ echo c.field echo d.field echo e.field +proc plus(a, b: GenInt4): GenInt4 = + for i in 0 ..< result.data.len: + result.data[i] = a.data[i] + b.data[i] + +echo plus(a, a).data + diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index a69b03426..e8740c591 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -315,6 +315,21 @@ when isMainModule: doAssert noYearDeser.year.isNone doAssert noYearDeser.engine.name == "V8" + # Issue #7433 + type + Obj2 = object + n1: int + n2: Option[string] + n3: bool + + var j = %*[ { "n1": 4, "n2": "ABC", "n3": true }, + { "n1": 1, "n3": false }, + { "n1": 1, "n2": "XYZ", "n3": false } ] + + let jDeser = j.to(seq[Obj2]) + doAssert jDeser[0].n2.get() == "ABC" + doAssert jDeser[1].n2.isNone() + # Table[T, Y] support. block: type diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index ae056a79f..8efc50086 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -367,4 +367,12 @@ suite "ttimes": check $(dt - 1.months) == "2017-02-15T00:00:00+00:00" dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc()) # This happens due to monthday overflow. It's consistent with Phobos. - check $(dt - 1.months) == "2017-03-03T00:00:00+00:00" \ No newline at end of file + check $(dt - 1.months) == "2017-03-03T00:00:00+00:00" + + test "compare datetimes": + var dt1 = now() + var dt2 = dt1 + check dt1 == dt2 + check dt1 <= dt2 + dt2 = dt2 + 1.seconds + check dt1 < dt2 diff --git a/tests/system/alloc.nim b/tests/system/talloc.nim index 7abefec2a..18396041d 100644 --- a/tests/system/alloc.nim +++ b/tests/system/talloc.nim @@ -8,7 +8,7 @@ x.dealloc() x = createU(int, 3) assert x != nil -x.free() +x.dealloc() x = create(int, 4) assert cast[ptr array[4, int]](x)[0] == 0 @@ -18,7 +18,7 @@ assert cast[ptr array[4, int]](x)[3] == 0 x = x.resize(4) assert x != nil -x.free() +x.dealloc() x = cast[ptr int](allocShared(100)) assert x != nil @@ -26,7 +26,7 @@ deallocShared(x) x = createSharedU(int, 3) assert x != nil -x.freeShared() +x.deallocShared() x = createShared(int, 3) assert x != nil @@ -37,7 +37,7 @@ assert cast[ptr array[3, int]](x)[2] == 0 assert x != nil x = cast[ptr int](x.resizeShared(2)) assert x != nil -x.freeShared() +x.deallocShared() x = create(int, 10) assert x != nil @@ -49,4 +49,9 @@ x = createShared(int, 1) assert x != nil x = x.resizeShared(1) assert x != nil -x.freeShared() +x.deallocShared() + +x = cast[ptr int](alloc0(125 shl 23)) +dealloc(x) +x = cast[ptr int](alloc0(126 shl 23)) +dealloc(x) diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim new file mode 100644 index 000000000..c8cab78a1 --- /dev/null +++ b/tests/system/talloc2.nim @@ -0,0 +1,37 @@ +const + nmax = 2*1024*1024*1024 + +proc test(n: int) = + var a = alloc0(9999) + var t = cast[ptr UncheckedArray[int8]](alloc(n)) + var b = alloc0(9999) + t[0] = 1 + t[1] = 2 + t[n-2] = 3 + t[n-1] = 4 + dealloc(a) + dealloc(t) + dealloc(b) + +# allocator adds 48 bytes to BigChunk +# BigChunk allocator edges at 2^n * (1 - s) for s = [1..32]/64 +proc test2(n: int) = + let d = n div 256 # cover edges and more + for i in countdown(128,1): + for j in [-4096, -64, -49, -48, -47, -32, 0, 4096]: + let b = n + j - i*d + if b>0 and b<=nmax: + test(b) + #echo b, ": ", getTotalMem(), " ", getOccupiedMem(), " ", getFreeMem() + +proc test3 = + var n = 1 + while n <= nmax: + test2(n) + n *= 2 + n = nmax + while n >= 1: + test2(n) + n = n div 2 + +test3() diff --git a/tests/system/io.nim b/tests/system/tio.nim index 3d4df806b..3d4df806b 100644 --- a/tests/system/io.nim +++ b/tests/system/tio.nim diff --git a/tests/system/params.nim b/tests/system/tparams.nim index 1358212f2..1358212f2 100644 --- a/tests/system/params.nim +++ b/tests/system/tparams.nim diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index ce36895a1..85228e9e7 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -1,5 +1,17 @@ discard """ - output:"" + output:'''1 +1 +2 +3 +11 +12 +13 +14 +15 +2 +3 +4 +''' """ # check high/low implementations @@ -20,3 +32,18 @@ doAssert high(float64) > low(float64) # bug #6710 var s = @[1] s.delete(0) + + +proc foo(a: openArray[int]) = + for x in a: echo x + +foo(toOpenArray([1, 2, 3], 0, 0)) + +foo(toOpenArray([1, 2, 3], 0, 2)) + +var arr: array[8..12, int] = [11, 12, 13, 14, 15] + +foo(toOpenArray(arr, 8, 12)) + +var seqq = @[1, 2, 3, 4, 5] +foo(toOpenArray(seqq, 1, 3)) diff --git a/tests/varres/tvarres1.nim b/tests/varres/tvarres1.nim index 849805768..5a5247142 100644 --- a/tests/varres/tvarres1.nim +++ b/tests/varres/tvarres1.nim @@ -1,7 +1,7 @@ discard """ file: "tvarres1.nim" line: 12 - errormsg: "address of 'bla' may not escape its stack frame" + errormsg: "'bla' escapes its stack frame; context: 'bla'" """ var diff --git a/tests/varres/tvarres_via_forwarding.nim b/tests/varres/tvarres_via_forwarding.nim new file mode 100644 index 000000000..8fd3dfcfd --- /dev/null +++ b/tests/varres/tvarres_via_forwarding.nim @@ -0,0 +1,12 @@ +discard """ + line: 10 + errormsg: "'y' escapes its stack frame; context: 'forward(y)'" +""" + +proc forward(x: var int): var int = result = x + +proc foo(): var int = + var y = 9 + result = forward(y) + +echo foo() diff --git a/tests/varres/twrong_parameter.nim b/tests/varres/twrong_parameter.nim new file mode 100644 index 000000000..8a363dd19 --- /dev/null +++ b/tests/varres/twrong_parameter.nim @@ -0,0 +1,16 @@ +discard """ + line: 10 + errormsg: "'x' is not the first parameter; context: 'x.field[0]'" +""" + +type + MyObject = object + field: array[2, int] + +proc forward(abc: int; x: var MyObject): var int = result = x.field[0] + +proc foo(): var int = + var y: MyObject + result = forward(45, y) + +echo foo() diff --git a/todo.txt b/todo.txt deleted file mode 100644 index 32bc32630..000000000 --- a/todo.txt +++ /dev/null @@ -1,71 +0,0 @@ -version 1.0 battle plan -======================= - -- let 'doAssert' analyse the expressions and produce more helpful output -- fix "high priority" bugs -- try to fix as many compiler crashes as reasonable - - -Not critical for 1.0 -==================== - -- introduce ``nkStmtListExpr`` for template/macro invokations to produce - better stack traces -- make 'break' not leave named blocks -- make FlowVar compatible to Futures -- make 'not nil' the default (produce warnings instead of errors for - a smooth migration path) -- case objects needs to be safe and need to support pattern matching - -- find a solution for the x.f[T](y) gotcha -- implement ``.delegate`` for .experimental -- annotation support for getType() -- make '--implicitStatic:on' the default; then we can also clean up the - 'static[T]' mess in the compiler! -- ``not`` or ``~`` for the effects system -- figure out why C++ bootstrapping is so much slower -- make 'nil' work for 'add': - - resizeString - - incrSeq - - addChar -- pragmas need 'bindSym' support -- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work - reliably with user-defined pragmas -- we need a magic thisModule symbol -- optimize 'genericReset'; 'newException' leads to code bloat - -- prevent 'alloc(TypeWithGCedMemory)'? -- map ``string`` and ``seq`` to ``std::string`` and ``std::vector`` -- macro support for '='; bind '=' to a memory region -- macros as type pragmas - - -Bugs -==== - -- VM: Pegs do not work at compile-time -- blocks can "export" an identifier but the CCG generates {} for them ... - - -GC -== - -- resizing of strings/sequences could take into account the memory that - is allocated - - -Concurrency -=========== - -- implement 'foo[1..4] = spawn(f[4..7])' - -Low priority: -- support for exception propagation? (hard to implement) -- the copying of the 'ref Promise' into the thead local storage only - happens to work due to the write barrier's implementation - - -CGEN -==== -- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC -- ``restrict`` pragma + backend support diff --git a/web/website.ini b/web/website.ini index 44f01c784..83a014821 100644 --- a/web/website.ini +++ b/web/website.ini @@ -58,7 +58,7 @@ srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits" srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;core/rlocks;pure/oids;pure/endians;pure/uri" srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "packages/docutils/rst;packages/docutils/rstast" -srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet" +srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet;pure/asyncstreams;pure/asyncfutures" srcdoc2: "pure/nativesockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" srcdoc2: "deprecated/pure/ftpclient;pure/collections/chains" srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/lenientops" |