diff options
39 files changed, 306 insertions, 123 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 7138b5f52..cd002eef1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1129,7 +1129,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType = result.align = 2 # default alignment result.id = getID() when debugIds: - RegisterId(result) + registerId(result) #if result.id < 2000 then # MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) @@ -1166,7 +1166,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType = if keepId: result.id = t.id else: - when debugIds: RegisterId(result) + when debugIds: registerId(result) result.sym = t.sym # backend-info should not be copied proc copySym(s: PSym, keepId: bool = false): PSym = @@ -1177,7 +1177,7 @@ proc copySym(s: PSym, keepId: bool = false): PSym = result.id = s.id else: result.id = getID() - when debugIds: RegisterId(result) + when debugIds: registerId(result) result.flags = s.flags result.magic = s.magic if s.kind == skModule: diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 2505bc687..64c1b717c 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -561,14 +561,31 @@ proc strTableContains(t: TStrTable, n: PSym): bool = proc strTableRawInsert(data: var TSymSeq, n: PSym) = var h: THash = n.name.h and high(data) - while data[h] != nil: - if data[h] == n: - # allowed for 'export' feature: - #InternalError(n.info, "StrTableRawInsert: " & n.name.s) - return - h = nextTry(h, high(data)) - assert(data[h] == nil) - data[h] = n + if sfImmediate notin n.flags: + # fast path: + while data[h] != nil: + if data[h] == n: + # allowed for 'export' feature: + #InternalError(n.info, "StrTableRawInsert: " & n.name.s) + return + h = nextTry(h, high(data)) + assert(data[h] == nil) + data[h] = n + else: + # slow path; we have to ensure immediate symbols are preferred for + # symbol lookups. + # consider the chain: foo (immediate), foo, bar, bar (immediate) + # then bar (immediate) gets replaced with foo (immediate) and the non + # immediate foo is picked! Thus we need to replace it with the first + # slot that has in fact the same identifier stored in it! + var favPos = -1 + while data[h] != nil: + if data[h] == n: return + if favPos < 0 and data[h].name.id == n.name.id: favPos = h + h = nextTry(h, high(data)) + assert(data[h] == nil) + data[h] = n + if favPos >= 0: swap data[h], data[favPos] proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = assert prevSym.name.h == newSym.name.h diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index be47ac0c4..031ab8d70 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = app(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = OnHeap + tmp2.s = if isRef: OnHeap else: OnStack tmp2.heapRoot = tmp.r expr(p, it.sons[1], tmp2) if d.k == locNone: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 6dfd25968..c31eb3121 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -339,4 +339,16 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = n.sons[0].sym.name, o.inSymChoice) if result != nil and result.kind == skStub: loadStub(result) - + +when false: + proc qualifiedLookUpPreferImmediate*(c: PContext, n: PNode, + flags = {checkUndeclared}): PSym = + var o: TOverloadIter + result = initOverloadIter(o, c, n) + var a = result + while a != nil: + if sfImmediate in a.flags: return a + a = nextOverloadIter(o, c, n) + if result == nil and checkUndeclared in flags: + localError(n.info, errUndeclaredIdentifier, n.considerAcc.s) + result = errorSym(c, n) diff --git a/compiler/parser.nim b/compiler/parser.nim index 7c740559c..ff3324b47 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -35,6 +35,7 @@ type lex*: TLexer # the lexer that is used for parsing tok*: TToken # the current token inPragma: int + inSemiStmtList: int proc parseAll*(p: var TParser): PNode proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) @@ -455,11 +456,13 @@ proc complexOrSimpleStmt(p: var TParser): PNode proc simpleExpr(p: var TParser, mode = pmNormal): PNode proc semiStmtList(p: var TParser, result: PNode) = + inc p.inSemiStmtList result.add(complexOrSimpleStmt(p)) while p.tok.tokType == tkSemiColon: getTok(p) optInd(p, result) result.add(complexOrSimpleStmt(p)) + dec p.inSemiStmtList result.kind = nkStmtListExpr proc parsePar(p: var TParser): PNode = @@ -1881,14 +1884,18 @@ proc parseStmt(p: var TParser): PNode = parMessage(p, errComplexStmtRequiresInd) result = ast.emptyNode else: - result = newNodeP(nkStmtList, p) - while true: - if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) - let a = simpleStmt(p) - if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - result.add(a) - if p.tok.tokType != tkSemiColon: break - getTok(p) + if p.inSemiStmtList > 0: + result = simpleStmt(p) + if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) + else: + result = newNodeP(nkStmtList, p) + while true: + if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) + let a = simpleStmt(p) + if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) + result.add(a) + if p.tok.tokType != tkSemiColon: break + getTok(p) proc parseAll(p: var TParser): PNode = result = newNodeP(nkStmtList, p) diff --git a/compiler/sem.nim b/compiler/sem.nim index e89e32f4e..00ac79716 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -83,7 +83,9 @@ proc commonType*(x, y: PType): PType = elif a.kind == tyTypeDesc: # turn any concrete typedesc into the abstract typedesc type if a.sons == nil: result = a - else: result = newType(tyTypeDesc, a.owner) + else: + result = newType(tyTypeDesc, a.owner) + rawAddSon(result, newType(tyNone, a.owner)) elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and a.kind == b.kind: # check for seq[empty] vs. seq[int] diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8b97f3685..c9d95e1bf 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -211,6 +211,7 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = makeTypeDesc(c, typ) + rawAddSon(typedesc, newTypeS(tyNone, c)) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d3cc1a12e..0871b7fb7 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -740,7 +740,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # we fill it out later. For magic generics like 'seq', it won't be filled # so we use tyEmpty instead of nil to not crash for strange conversions # like: mydata.seq - rawAddSon(s.typ, newTypeS(tyEmpty, c)) + rawAddSon(s.typ, newTypeS(tyNone, c)) s.ast = a inc c.inGenericContext var body = semTypeNode(c, a.sons[2], nil) @@ -748,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if body != nil: body.sym = s body.size = -1 # could not be computed properly - s.typ.sons[sonsLen(s.typ) - 1] = body + s.typ.sons[sonsLen(s.typ) - 1] = body popOwner() closeScope(c) elif a.sons[2].kind != nkEmpty: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 408b1b62e..44e414e9c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -681,12 +681,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) - # produces tySequence(tyGenericParam, null). + # produces tySequence(tyGenericParam, tyNone). # This also seems to be true when creating aliases # like: type myseq = distinct seq. # Maybe there is another better place to associate # the seq type class with the seq identifier. - if paramType.kind == tySequence and paramType.lastSon == nil: + if paramType.kind == tySequence and paramType.lastSon.kind == tyNone: let typ = c.newTypeWithSons(tyBuiltInTypeClass, @[newTypeS(paramType.kind, c)]) result = addImplicitGeneric(typ) @@ -885,8 +885,12 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) + elif s.typ.kind != tyGenericBody: + #we likely got code of the form TypeA[TypeB] where TypeA is + #not generic. + localError(n.info, errNoGenericParamsAllowedForX, s.name.s) + return newOrPrevType(tyError, prev, c) else: - internalAssert s.typ.kind == tyGenericBody var m = newCandidate(c, s, n) matches(c, n, copyTree(n), m) @@ -1133,15 +1137,26 @@ proc processMagicType(c: PContext, m: PSym) = of mNil: setMagicType(m, tyNil, ptrSize) of mExpr: setMagicType(m, tyExpr, 0) of mStmt: setMagicType(m, tyStmt, 0) - of mTypeDesc: setMagicType(m, tyTypeDesc, 0) + of mTypeDesc: + setMagicType(m, tyTypeDesc, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(m, tyEmpty, 0) - of mArray: setMagicType(m, tyArray, 0) - of mOpenArray: setMagicType(m, tyOpenArray, 0) - of mVarargs: setMagicType(m, tyVarargs, 0) - of mRange: setMagicType(m, tyRange, 0) - of mSet: setMagicType(m, tySet, 0) - of mSeq: setMagicType(m, tySequence, 0) - of mOrdinal: setMagicType(m, tyOrdinal, 0) + of mArray: + setMagicType(m, tyArray, 0) + of mOpenArray: + setMagicType(m, tyOpenArray, 0) + of mVarargs: + setMagicType(m, tyVarargs, 0) + of mRange: + setMagicType(m, tyRange, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) + of mSet: + setMagicType(m, tySet, 0) + of mSeq: + setMagicType(m, tySequence, 0) + of mOrdinal: + setMagicType(m, tyOrdinal, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: discard else: localError(m.info, errTypeExpected) @@ -1165,8 +1180,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = typ = semTypeNode(c, constraint, nil) if typ.kind != tyStatic or typ.len == 0: if typ.kind == tyTypeDesc: - if typ.len == 0: - typ = newTypeS(tyTypeDesc, c) + if typ.sons[0].kind == tyNone: + typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) else: typ = semGenericConstraints(c, typ) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 1158335a8..a07d91241 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -358,7 +358,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = if lookup != nil: result = lookup if tfUnresolved in t.flags: result = result.base - elif t.sonsLen > 0: + elif t.sons[0].kind != tyNone: result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0])) of tyUserTypeClass: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d269e9e69..f9200ea0c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -562,10 +562,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = #if result < isGeneric: result = isNone if result notin {isNone, isGeneric}: result = typeRangeRel(f, a) - elif skipTypes(f, {tyRange}).kind == a.kind: - result = isIntConv - elif isConvertibleToRange(skipTypes(f, {tyRange}), a): - result = isConvertible # a convertible to f + else: + if skipTypes(f, {tyRange}).kind == a.kind: + result = isIntConv + elif isConvertibleToRange(skipTypes(f, {tyRange}), a): + result = isConvertible # a convertible to f of tyInt: result = handleRange(f, a, tyInt8, tyInt32) of tyInt8: result = handleRange(f, a, tyInt8, tyInt8) of tyInt16: result = handleRange(f, a, tyInt8, tyInt16) @@ -636,7 +637,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyOrdinal: if isOrdinalType(a): var x = if a.kind == tyOrdinal: a.sons[0] else: a - if f.sonsLen == 0: + if f.sons[0].kind == tyNone: result = isGeneric else: result = typeRel(c, f.sons[0], x) @@ -736,7 +737,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericInst: let roota = a.skipGenericAlias let rootf = f.skipGenericAlias - if a.kind == tyGenericInst and roota.base == rootf.base : + if a.kind == tyGenericInst and roota.base == rootf.base: for i in 1 .. rootf.sonsLen-2: let ff = rootf.sons[i] let aa = roota.sons[i] @@ -845,7 +846,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # by a tyTypeDesc params. Unfortunately, this requires more substantial # changes in semtypinst and elsewhere. if a.kind == tyTypeDesc: - if f.sons == nil or f.sons.len == 0: + if f.sonsLen == 0: result = isGeneric else: internalAssert a.sons != nil and a.sons.len > 0 @@ -854,7 +855,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: result = isNone else: - if f.sonsLen > 0: + if f.sonsLen > 0 and f.sons[0].kind != tyNone: result = typeRel(c, f.lastSon, a) else: result = isGeneric @@ -883,7 +884,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = var prev = PType(idTableGet(c.bindings, f)) if prev == nil: if a.kind == tyTypeDesc: - if f.sonsLen == 0: + if f.sons[0].kind == tyNone: result = isGeneric else: result = typeRel(c, f.sons[0], a.sons[0]) diff --git a/compiler/types.nim b/compiler/types.nim index 812cd4e93..db75cd3c0 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -47,7 +47,7 @@ proc equalParams*(a, b: PNode): TParamsEquality # returns whether the parameter lists of the procs a, b are exactly the same proc isOrdinalType*(t: PType): bool proc enumHasHoles*(t: PType): bool -# XXX it is WRONG to include tyTypeDesc here as that might not have any child! + const abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, tyTypeDesc} @@ -1258,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool = return true if t.kind == tyTypeDesc: - if t.sonsLen == 0: return true + if t.sons[0].kind == tyNone: return true if containsGenericTypeIter(t.base, closure): return true return false diff --git a/compiler/vm.nim b/compiler/vm.nim index 61881a897..10ac7aaaf 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -14,7 +14,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, - parser, vmdeps, idents, trees, renderer, options + parser, vmdeps, idents, trees, renderer, options, transf from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -1078,6 +1078,7 @@ proc execute(c: PCtx, start: int): PNode = result = rawExecute(c, start, tos) proc evalStmt*(c: PCtx, n: PNode) = + let n = transformExpr(c.module, n) let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': @@ -1085,6 +1086,7 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = + let n = transformExpr(c.module, n) let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) @@ -1127,6 +1129,7 @@ proc myProcess(c: PPassContext, n: PNode): PNode = const evalPass* = makePass(myOpen, nil, myProcess, myProcess) proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = + let n = transformExpr(module, n) setupGlobalCtx(module) var c = globalCtx c.mode = mode diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index be32f990f..d0e8dacf3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -946,6 +946,14 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar +proc setSlot(c: PCtx; v: PSym) = + # XXX generate type initialization here? + if v.position == 0: + v.position = c.prc.maxSlots + c.prc.slots[v.position] = (inUse: true, + kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) + inc c.prc.maxSlots + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: @@ -973,8 +981,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = gen(c, ri, tmp) c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position) else: + if s.kind == skForVar and c.mode == emRepl: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and - s.kind in {skParam,skResult,skForVar}) + s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) gen(c, ri, dest) else: @@ -1024,7 +1033,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = if s.isGlobal: if sfCompileTime in s.flags or c.mode == emRepl: discard - else: + elif s.position == 0: cannotEval(n) if s.position == 0: if sfImportc in s.flags: c.importcSym(n.info, s) @@ -1035,8 +1044,9 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = else: c.gABx(n, opcLdGlobal, dest, s.position) else: + if s.kind == skForVar and c.mode == emRepl: c.setSlot s if s.position > 0 or (s.position == 0 and - s.kind in {skParam,skResult,skForVar}): + s.kind in {skParam,skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) else: @@ -1045,7 +1055,6 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = else: # see tests/t99bott for an example that triggers it: cannotEval(n) - #InternalError(n.info, s.name.s & " " & $s.position) proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = @@ -1123,14 +1132,6 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkCurly, info, t) else: internalError("getNullValue: " & $t.kind) -proc setSlot(c: PCtx; v: PSym) = - # XXX generate type initialization here? - if v.position == 0: - v.position = c.prc.maxSlots - c.prc.slots[v.position] = (inUse: true, - kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) - inc c.prc.maxSlots - proc genVarSection(c: PCtx; n: PNode) = for a in n: if a.kind == nkCommentStmt: continue diff --git a/doc/manual.txt b/doc/manual.txt index da229d169..520e4f62e 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2231,6 +2231,8 @@ Instead of: Using statement --------------- +**Warning**: The ``using`` statement is highly experimental! + The `using statement`:idx: provides syntactic convenience for procs that heavily use a single contextual parameter. When applied to a variable or a constant, it will instruct Nimrod to automatically consider the used symbol as diff --git a/doc/tut2.txt b/doc/tut2.txt index 581239cc7..6738c5551 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -528,7 +528,7 @@ containers: proc newNode*[T](data: T): PBinaryTree[T] = # constructor for a node new(result) - result.dat = data + result.data = data proc add*[T](root: var PBinaryTree[T], n: PBinaryTree[T]) = # insert a node into the tree @@ -569,7 +569,7 @@ containers: var root: PBinaryTree[string] # instantiate a PBinaryTree with ``string`` - add(root, newNode("hallo")) # instantiates ``newNode`` and ``add`` + add(root, newNode("hello")) # instantiates ``newNode`` and ``add`` add(root, "world") # instantiates the second ``add`` proc for str in preorder(root): stdout.writeln(str) diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim index d012f677f..dc8a808f2 100644 --- a/lib/impure/db_mongo.nim +++ b/lib/impure/db_mongo.nim @@ -58,7 +58,7 @@ proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {. ## be established. init(result) - let x = connect(result, host, port.cint) + let x = client(result, host, port.cint) if x != 0'i32: dbError(result, "cannot open: " & host) @@ -119,7 +119,7 @@ proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {. ## the generated OID for the ``_id`` field. result = genOid() var x = jsonToBSon(data, result) - insert(db, namespace, x) + insert(db, namespace, x, nil) destroy(x) proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {. diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index f13cadaa2..96afc6f4f 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -689,5 +689,5 @@ when isMainModule: server.listen() d.register(server) - while d.poll(-1): nil + while d.poll(-1): discard diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 73da274b9..40ae57b5a 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -189,6 +189,16 @@ template dollarImpl(): stmt {.dirty.} = proc `$`*[A, B](t: TTable[A, B]): string = ## The `$` operator for hash tables. dollarImpl() + +proc `==`*[A, B](s, t: TTable[A, B]): bool = + s.counter == t.counter and s.data == t.data + +proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] = + ## Index the collection with the proc provided. + # TODO: As soon as supported, change collection: A to collection: A[B] + result = initTable[C, B]() + for item in collection: + result[index(item)] = item # ------------------------------ ordered table ------------------------------ diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 060f0e386..c38eb7063 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -480,7 +480,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, if htmlTag(x.elemName) in {tagOption, tagOptgroup}: errors.add(expected(x, result)) break - else: nil + else: discard result.addNode(parse(x, errors)) of xmlElementEnd: if cmpIgnoreCase(x.elemName, result.tag) == 0: @@ -547,7 +547,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = var u = entityToUtf8(x.rawData) if u.len != 0: result = newText(u) next(x) - of xmlEof: nil + of xmlEof: discard proc parseHtml*(s: PStream, filename: string, errors: var seq[string]): PXmlNode = diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim index 750c98516..c1b519b0b 100644 --- a/lib/pure/irc.nim +++ b/lib/pure/irc.nim @@ -476,12 +476,12 @@ when isMainModule: var client = irc("amber.tenthbit.net", nick="TestBot1234", joinChans = @["#flood"]) client.connect() - while True: + while true: var event: TIRCEvent if client.poll(event): case event.typ of EvConnected: - nil + discard of EvDisconnected: break of EvMsg: diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index b57e0c45a..2db7fa660 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -54,7 +54,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {. try: discard parseutils.parseInt(s, x, 0) except EOverflow: - nil + discard if x in validRange: value = x when isMainModule: diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index fbe0dda95..b3e74d2a1 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -28,7 +28,7 @@ proc hexbyte*(hex: char): int = of '0'..'9': result = (ord(hex) - ord('0')) of 'a'..'f': result = (ord(hex) - ord('a') + 10) of 'A'..'F': result = (ord(hex) - ord('A') + 10) - else: nil + else: discard proc parseOid*(str: cstring): TOid = ## parses an OID. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 245a8446b..bb70f28b6 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1078,9 +1078,9 @@ when defined(windows): while true: var eend = strEnd(e) add(environment, $e) - e = cast[CString](cast[TAddress](eend)+1) + e = cast[cstring](cast[TAddress](eend)+1) if eend[1] == '\0': break - discard FreeEnvironmentStringsA(env) + discard freeEnvironmentStringsA(env) envComputed = true else: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 9975bfcb3..6df85bbc6 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -455,7 +455,7 @@ when defined(Windows) and not defined(useNimRtl): ee, wwd, si, procInfo) else: success = winlean.createProcessA(nil, - cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo) + cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo) let lastError = osLastError() if poParentStreams notin options: @@ -534,7 +534,7 @@ when defined(Windows) and not defined(useNimRtl): NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) else: var res = winlean.createProcessA(nil, command, nil, nil, 0, - NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) + NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) if res == 0: osError(osLastError()) else: diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 5970f2090..4b25babec 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -149,7 +149,7 @@ proc readRow*(my: var TCsvParser, columns = 0): bool = of '\c': my.bufpos = handleCR(my, my.bufpos) of '\l': my.bufpos = handleLF(my, my.bufpos) else: break - of '\0': nil + of '\0': discard else: error(my, my.bufpos, my.sep & " expected") break diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 31951e966..3f9686e1e 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -79,7 +79,7 @@ proc handleHexChar(c: var TSqlLexer, xi: var int) = xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) else: - nil + discard proc handleOctChar(c: var TSqlLexer, xi: var int) = if c.buf[c.bufpos] in {'0'..'7'}: @@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) = of '+': if not trailingPlusMinus and buf[pos+1] notin operators and tok.literal.len > 0: break - of '*', '<', '>', '=': nil + of '*', '<', '>', '=': discard else: break add(tok.literal, buf[pos]) inc(pos) @@ -1120,7 +1120,7 @@ proc rs(n: PSqlNode, s: var string, indent: int, proc ra(n: PSqlNode, s: var string, indent: int) = if n == nil: return case n.kind - of nkNone: nil + of nkNone: discard of nkIdent: if allCharsInSet(n.strVal, {'\33'..'\127'}): s.add(n.strVal) diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 16bbe1455..8b8bb3b03 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -96,7 +96,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = ## &entity; errors.add(errorMsg(x, "unknown entity: " & x.entityName)) next(x) - of xmlEof: nil + of xmlEof: discard proc parseXml*(s: PStream, filename: string, errors: var seq[string]): PXmlNode = @@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string, of xmlElementOpen, xmlElementStart: result = parse(x, errors) break - of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it + of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it of xmlError: errors.add(errorMsg(x)) else: diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index ee5fe0647..5b641185e 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -195,14 +195,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -300,7 +300,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: CString, + proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} diff --git a/lib/wrappers/mongo.nim b/lib/wrappers/mongo.nim index 6673e8ddf..098b4f4d3 100644 --- a/lib/wrappers/mongo.nim +++ b/lib/wrappers/mongo.nim @@ -109,11 +109,12 @@ type cur*: cstring dataSize*: cint finished*: TBsonBool - stack*: array[0..32 - 1, cint] + ownsData*: TBsonBool + err*: cint + stackSize*: cint stackPos*: cint - err*: cint ## Bitfield representing errors or warnings on this buffer - errstr*: cstring ## A string representation of the most recent error - ## or warning. + stackPtr*: ptr csize + stack*: array[0..32 - 1, csize] TDate* = int64 @@ -141,6 +142,7 @@ proc print*(TBson: cstring, depth: cint) {.stdcall, importc: "bson_print_raw", dynlib: bsondll.} ## Print a string representation of a BSON object up to `depth`. + proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data", dynlib: bsondll.} ## Return a pointer to the raw buffer stored by this bson object. @@ -590,19 +592,30 @@ type hosts*: ptr THostPort ## List of host/ports given by the replica set name*: cstring ## Name of the replica set. primary_connected*: TBsonBool ## Primary node connection status. + + TWriteConcern*{.pure, final.} = object ## mongo_write_concern + w*: cint + wtimeout*: cint + j*: cint + fsync*: cint + mode*: cstring + cmd*: TBSon TMongo*{.pure, final.} = object ## mongo - primary*: ptr THostPort ## Primary connection info. - replset*: ptr TReplSet ## replset object if connected to a replica set. - sock*: cint ## Socket file descriptor. - flags*: cint ## Flags on this connection object. - conn_timeout_ms*: cint ## Connection timeout in milliseconds. - op_timeout_ms*: cint ## Read and write timeout in milliseconds. - connected*: TBsonBool ## Connection status. - err*: TError ## Most recent driver error code. - errstr*: array[0..128 - 1, char] ## String version of most recent driver error code. - lasterrcode*: cint ## getlasterror code given by the server on error. - lasterrstr*: cstring ## getlasterror string generated by server. + primary*: ptr THostPort ## Primary connection info. + replset*: ptr TReplSet ## replset object if connected to a replica set. + sock*: cint ## Socket file descriptor. + flags*: cint ## Flags on this connection object. + conn_timeout_ms*: cint ## Connection timeout in milliseconds. + op_timeout_ms*: cint ## Read and write timeout in milliseconds. + max_bson_size*: cint ## Largest BSON object allowed on this connection. + connected*: TBsonBool ## Connection status. + write_concern*: TWriteConcern ## The default write concern. + err*: TError ## Most recent driver error code. + errcode*: cint ## Most recent errno or WSAGetLastError(). + errstr*: array[0..128 - 1, char] ## String version of most recent driver error code. + lasterrcode*: cint ## getlasterror code given by the server on error. + lasterrstr*: array[0..128 - 1, char] ## getlasterror string generated by server. TCursor*{.pure, final.} = object ## cursor reply*: ptr TReply ## reply is owned by cursor @@ -654,7 +667,11 @@ proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.} proc connect*(conn: var TMongo, host: cstring = defaultHost, port: cint = defaultPort): cint {.stdcall, - importc: "mongo_connect", dynlib: mongodll.} + importc: "mongo_connect", dynlib: mongodll, deprecated.} + ## Connect to a single MongoDB server. +proc client*(conn: var TMongo, host: cstring = defaultHost, + port: cint = defaultPort): cint {.stdcall, + importc: "mongo_client", dynlib: mongodll.} ## Connect to a single MongoDB server. proc replsetInit*(conn: var TMongo, name: cstring){.stdcall, @@ -714,7 +731,8 @@ proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy", ## You must always call this function when finished with the connection ## object. -proc insert*(conn: var TMongo, ns: cstring, data: var TBson): cint{.stdcall, +proc insert*(conn: var TMongo, ns: cstring, data: var TBson, + custom_write_concern: ptr TWriteConcern): cint{.stdcall, importc: "mongo_insert", dynlib: mongodll, discardable.} ## Insert a BSON document into a MongoDB server. This function ## will fail if the supplied BSON struct is not UTF-8 or if diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim new file mode 100644 index 000000000..f374d5504 --- /dev/null +++ b/tests/collections/ttables.nim @@ -0,0 +1,22 @@ +import tables + +doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table" + +var tbl1 = initTable[int, int]() +tbl1.add(1,1) +tbl1.add(2,2) +doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table" + +type + TElem = object + foo: int + bar: string + +let + elem1 = TElem(foo: 1, bar: "bar") + elem2 = TElem(foo: 2, bar: "baz") + +var tbl2 = initTable[string, TElem]() +tbl2.add("bar", elem1) +tbl2.add("baz", elem2) +doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table" diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim index 8149ec4b8..816e58cb1 100644 --- a/tests/exprs/tstmtexprs.nim +++ b/tests/exprs/tstmtexprs.nim @@ -1,5 +1,6 @@ discard """ - output: '''(bar: bar) + output: '''24 +(bar: bar) 1244 6 abcdefghijklmnopqrstuvwxyz @@ -8,6 +9,10 @@ abcdefghijklmnopqrstuvwxyz import strutils +const fac4 = (var x = 1; for i in 1..4: x *= i; x) + +echo fac4 + when true: proc test(foo: proc (x, y: int): bool) = echo foo(5, 5) diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim index bd7bded28..6f2b8a1fe 100644 --- a/tests/gc/gcleak4.nim +++ b/tests/gc/gcleak4.nim @@ -6,7 +6,7 @@ when defined(GC_setMaxPause): GC_setMaxPause 2_000 type - TExpr = object ## abstract base class for an expression + TExpr = object {.inheritable.} ## abstract base class for an expression PLiteral = ref TLiteral TLiteral = object of TExpr x: int diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim new file mode 100644 index 000000000..9e2948729 --- /dev/null +++ b/tests/gc/gcleak5.nim @@ -0,0 +1,25 @@ +discard """ + output: "success" +""" + +import os, times + +proc main = + var i = 0 + for ii in 0..50_000: + #while true: + var t = getTime() + var g = t.getGMTime() + #echo isOnStack(addr g) + + if i mod 100 == 0: + let om = getOccupiedMem() + #echo "memory: ", om + if om > 100_000: quit "leak" + + inc(i) + sleep(1) + + echo "success" + +main() diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim new file mode 100644 index 000000000..865dc436a --- /dev/null +++ b/tests/macros/tdebugstmt.nim @@ -0,0 +1,29 @@ +discard """ + output: '''a[0]: 42 +a[1]: 45 +x: some string''' +""" + +import macros + +macro debug(n: varargs[expr]): stmt = + # `n` is a Nimrod AST that contains the whole macro invocation + # this macro returns a list of statements: + result = newNimNode(nnkStmtList, n) + # iterate over any argument that is passed to this macro: + for i in 0..n.len-1: + # add a call to the statement list that writes the expression; + # `toStrLit` converts an AST to its string representation: + add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) + # add a call to the statement list that writes ": " + add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) + # add a call to the statement list that writes the expressions value: + add(result, newCall("writeln", newIdentNode("stdout"), n[i])) + +var + a: array [0..10, int] + x = "some string" +a[0] = 42 +a[1] = 45 + +debug(a[0], a[1], x) diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 37de1262f..7ab652c82 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -1,14 +1,8 @@ -import unittest import algorithm -suite "product": - test "empty input": - check product[int](newSeq[seq[int]]()) == newSeq[seq[int]]() - test "bit more empty input": - check product[int](@[newSeq[int](), @[], @[]]) == newSeq[seq[int]]() - test "a simple case of one element": - check product(@[@[1,2]]) == @[@[1,2]] - test "two elements": - check product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]] - test "three elements": - check product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]] +doAssert product[int](newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" +doAssert product[int](@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" +doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element" +doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements" +doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements" +doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" diff --git a/tests/template/tprefer_immediate.nim b/tests/template/tprefer_immediate.nim new file mode 100644 index 000000000..578f447b0 --- /dev/null +++ b/tests/template/tprefer_immediate.nim @@ -0,0 +1,17 @@ +discard """ + output: '''immediate''' +""" + +# Test that immediate templates are preferred over non-immediate templates + +template foo(a, b: expr) = echo "foo expr" + +template foo(a, b: int) = echo "foo int" +template foo(a, b: float) = echo "foo float" +template foo(a, b: string) = echo "foo string" +template foo(a, b: expr) {.immediate.} = echo "immediate" +template foo(a, b: bool) = echo "foo bool" +template foo(a, b: char) = echo "foo char" + +foo(undeclaredIdentifier, undeclaredIdentifier2) + diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 5dd841447..442dd1212 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -123,9 +123,14 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gcleak2" test "gctest" test "gcleak3" + test "gcleak4" + test "gcleak5" test "weakrefs" test "cycleleak" test "closureleak" + test "refarrayleak" + test "stackrefleak" + # ------------------------- threading tests ----------------------------------- diff --git a/todo.txt b/todo.txt index fd424ad5e..738e9b3fa 100644 --- a/todo.txt +++ b/todo.txt @@ -1,13 +1,11 @@ version 0.9.4 ============= -- fix macros\tstringinterp.nim -- test and fix showoff; add debug example to showoff -- test and fix stdlib -- test and fix misc - fix GC issues +- fix macros\tstringinterp.nim +- test and fix showoff - test C source code generation -- test and fix closures +- fix closures - test and fix exception handling - implement 'union' and 'bits' pragmas @@ -22,7 +20,6 @@ Bugs - compilation of niminst takes way too long. looks like a regression - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead -- sneaking with qualifiedLookup() is really broken! - blocks can "export" an identifier but the CCG generates {} for them ... - osproc execProcesses can deadlock if all processes fail (as experienced in c++ mode) |