diff options
290 files changed, 3778 insertions, 1645 deletions
diff --git a/changelog.md b/changelog.md index 7cc2a3b63..6dbfa1a2d 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,7 @@ #### Breaking changes in the standard library +- `osproc.execProcess` now also takes a `workingDir` parameter. #### Breaking changes in the compiler @@ -61,6 +62,10 @@ proc enumToString*(enums: openArray[enum]): string = - Added `system.typeof` for more control over how `type` expressions can be deduced. +- Added `macros.isInstantiationOf` for checking if the proc symbol + is instantiation of generic proc symbol. + + ### Library changes - The string output of `macros.lispRepr` proc has been tweaked @@ -84,7 +89,9 @@ proc enumToString*(enums: openArray[enum]): string = ### Language additions - Vm suport for float32<->int32 and float64<->int64 casts was added. - +- There is a new pragma block `noSideEffect` that works like + the `gcsafe` pragma block. +- added os.getCurrentProcessId() ### Language changes @@ -100,5 +107,6 @@ proc enumToString*(enums: openArray[enum]): string = documentation. ### Compiler changes +- The deprecated `fmod` proc is now unavailable on the VM'. ### Bugfixes diff --git a/compiler/ast.nim b/compiler/ast.nim index bb0f95acd..40a05e6bf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -627,7 +627,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, + mMove, mWasMoved, mDestroy, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -656,14 +656,15 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf + mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, + mSymIsInstantiationOf # things that we can evaluate safely at compile time, even if not asked for it: const ctfeWhitelist* = {mNone, mUnaryLt, mSucc, mPred, mInc, mDec, mOrd, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, - mArrGet, mArrPut, mAsgn, + mArrGet, mArrPut, mAsgn, mDestroy, mIncl, mExcl, mCard, mChr, mAddI, mSubI, mMulI, mDivI, mModI, mAddF64, mSubF64, mMulF64, mDivF64, @@ -756,8 +757,6 @@ type OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section OnStack, # location is on hardware stack - OnStackShadowDup, # location is on the stack but also replicated - # on the shadow stack OnHeap # location is on heap or global # (reference counting needed) TLocFlags* = set[TLocFlag] @@ -767,7 +766,6 @@ type flags*: TLocFlags # location's flags lode*: PNode # Node where the location came from; can be faked r*: Rope # rope value of location (code generators) - dup*: Rope # duplicated location for precise stack scans # ---------------- end of backend information ------------------------------ @@ -898,6 +896,8 @@ type loc*: TLoc typeInst*: PType # for generic instantiations the tyGenericInst that led to this # type. + uniqueId*: int # due to a design mistake, we need to keep the real ID here as it + # required by the --incremental:on mode. TPair* = object key*, val*: RootRef @@ -1268,6 +1268,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = result.size = -1 result.align = -1 # default alignment result.id = getID() + result.uniqueId = result.id result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) @@ -1341,15 +1342,12 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType = proc exactReplica*(t: PType): PType = copyType(t, t.owner, true) -proc copySym*(s: PSym, keepId: bool = false): PSym = +proc copySym*(s: PSym): PSym = result = newSym(s.kind, s.name, s.owner, s.info, s.options) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ - if keepId: - result.id = s.id - else: - result.id = getID() - when debugIds: registerId(result) + result.id = getID() + 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 b716882dc..b2671d81e 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -254,21 +254,26 @@ proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int, if n == nil: result = rope("null") elif containsOrIncl(marker, n.id): - result = "\"$1 @$2\"" % [rope(n.name.s), rope( - strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] + result = "\"$1\"" % [rope(n.name.s)] else: var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1) result = ropeConstr(indent, [rope("kind"), makeYamlString($n.kind), rope("name"), makeYamlString(n.name.s), - rope("typ"), typeToYamlAux(conf, n.typ, marker, - indent + 2, maxRecDepth - 1), + #rope("typ"), typeToYamlAux(conf, n.typ, marker, + # indent + 2, maxRecDepth - 1), rope("info"), lineInfoToStr(conf, n.info), rope("flags"), flagsToStr(n.flags), rope("magic"), makeYamlString($n.magic), rope("ast"), ast, rope("options"), flagsToStr(n.options), rope("position"), - rope(n.position)]) + rope(n.position), + rope("k"), makeYamlString($n.loc.k), + rope("storage"), makeYamlString($n.loc.storage), + rope("flags"), makeYamlString($n.loc.flags), + rope("r"), n.loc.r, + rope("lode"), treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1) + ]) proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, maxRecDepth: int): Rope = @@ -394,10 +399,16 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; of nkStrLit..nkTripleStrLit: addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: - addf(result, ",$N$1\"sym\": $2_$3", - [istr, rope(n.sym.name.s), rope(n.sym.id)]) - # [istr, symToYaml(n.sym, indent, maxRecDepth), - # rope(n.sym.id)]) + let s = n.sym + addf(result, ",$N$1\"sym\": $2_$3 k: $4 storage: $5 flags: $6 r: $7", + [istr, rope(s.name.s), rope(s.id), + rope($s.loc.k), + rope($s.loc.storage), + rope($s.loc.flags), + s.loc.r + ]) +# [istr, symToYaml(conf, n.sym, indent, maxRecDepth), +# rope(n.sym.id)]) if renderType and n.sym.typ != nil: addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)]) of nkIdent: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1b70ee41b..59ef05f9c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -31,10 +31,18 @@ proc intLiteral(i: BiggestInt): Rope = result = ~"(IL64(-9223372036854775807) - IL64(1))" proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = - if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil") case n.kind of nkCharLit..nkUInt64Lit: - case skipTypes(ty, abstractVarRange).kind + var k: TTypeKind + if ty != nil: + k = skipTypes(ty, abstractVarRange).kind + else: + case n.kind + of nkCharLit: k = tyChar + of nkUInt64Lit: k = tyUInt64 + of nkInt64Lit: k = tyInt64 + else: k = tyNil # don't go into the case variant that uses 'ty' + case k of tyChar, tyNil: result = intLiteral(n.intVal) of tyBool: @@ -46,8 +54,8 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = result = "(($1) $2)" % [getTypeDesc(p.module, ty), intLiteral(n.intVal)] of nkNilLit: - let t = skipTypes(ty, abstractVarRange) - if t.kind == tyProc and t.callConv == ccClosure: + let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind + if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) if id == p.module.labels: @@ -59,7 +67,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - case skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind + let k = if ty == nil: tyString + else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind + case k of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: @@ -168,32 +178,12 @@ proc canMove(p: BProc, n: PNode): bool = # echo n.info, " optimized ", n # result = false -proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = +proc genRefAssign(p: BProc, dest, src: TLoc) = if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: - # location is on heap - # now the writer barrier is inlined for performance: - # - # if afSrcIsNotNil in flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)]) - # elif afSrcIsNil notin flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)]) - # if afDestIsNotNil in flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)]) - # elif afDestIsNil notin flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)]) - # lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)]) - if canFormAcycle(dest.t): - linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", - addrLoc(p.config, dest), rdLoc(src)) - else: - linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n", - addrLoc(p.config, dest), rdLoc(src)) + linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", + addrLoc(p.config, dest), rdLoc(src)) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, dest), rdLoc(src)) @@ -261,7 +251,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # (for objects, etc.): if p.config.selectedGC == gcDestructors: linefmt(p, cpsStmts, - "$1.len = $2.len; $1.p = $2.p;$n", + "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: @@ -286,12 +276,12 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic}) case ty.kind of tyRef: - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) of tySequence: if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode): - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(p.config, dest), rdLoc(src), @@ -300,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode): - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) else: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) @@ -315,16 +305,16 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", addrLoc(p.config, dest), rdLoc(src)) of tyProc: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): # optimize closure assignment: let a = optAsgnLoc(dest, dest.t, "ClE_0".rope) let b = optAsgnLoc(src, dest.t, "ClE_0".rope) - genRefAssign(p, a, b, flags) + genRefAssign(p, a, b) linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", rdLoc(dest), rdLoc(src)) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyTuple: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags) else: genGenericAsgn(p, dest, src, flags) else: @@ -335,7 +325,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif not isObjLackingTypeField(ty): genGenericAsgn(p, dest, src, flags) - elif needsComplexAssignment(ty): + elif containsGarbageCollectedRef(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: discard getTypeDesc(p.module, ty) internalAssert p.config, ty.n != nil @@ -345,7 +335,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyArray: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, @@ -354,7 +344,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyOpenArray, tyVarargs: # open arrays are always on the stack - really? What if a sequence is # passed to an open array? - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", addrLoc(p.config, dest), addrLoc(p.config, src), @@ -1148,14 +1138,14 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genTypeInfo(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so # use 'genRefAssign' for the seq. - genRefAssign(p, a, call, {}) + genRefAssign(p, a, call) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, e.sons[2], OnHeap) getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) dest.r = ropecg(p.module, "$1$3[$2]", rdLoc(a), tmpL.r, dataField(p)) - genAssignment(p, dest, b, {needToCopy, afDestIsNil}) + genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = @@ -1359,9 +1349,17 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, tmp) elif d.k == locNone: getTemp(p, n.typ, d) - # generate call to newSeq before adding the elements per hand: - genNewSeqAux(p, dest[], intLiteral(sonsLen(n)), - optNilSeqs notin p.options and n.len == 0) + + let l = intLiteral(sonsLen(n)) + if p.config.selectedGC == gcDestructors: + let seqtype = n.typ + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + # generate call to newSeq before adding the elements per hand: + genNewSeqAux(p, dest[], l, + optNilSeqs notin p.options and n.len == 0) for i in countup(0, sonsLen(n) - 1): initLoc(arr, locExpr, n[i], OnHeap) arr.r = ropecg(p.module, "$1$3[$2]", rdLoc(dest[]), intLiteral(i), dataField(p)) @@ -1384,7 +1382,13 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: let L = int(lengthOrd(p.config, n.sons[1].typ)) - genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0) + if p.config.selectedGC == gcDestructors: + let seqtype = n.typ + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + rdLoc d, rope L, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0) initLocExpr(p, n.sons[1], a) # bug #5007; do not produce excessive C source code: if L < 10: @@ -1394,7 +1398,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) else: var i: TLoc getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) @@ -1405,7 +1409,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -2054,6 +2058,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) of mMove: genMove(p, e, d) + of mDestroy: discard "ignore calls to the default destructor" of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & " 'toOpenArray' is only valid within a call expression") diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index ccfa49a1d..904d01e81 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -54,7 +54,7 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = result = getTempName(m) addf(m.s[cfsData], "static const struct {$n" & - " NI cap; void* allocator; NIM_CHAR data[$2];$n" & + " NI cap; void* allocator; NIM_CHAR data[$2+1];$n" & "} $1 = { $2, NIM_NIL, $3 };$n", [result, rope(len(s)), makeCString(s)]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 3665a7e85..e83b80b7c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -307,11 +307,13 @@ proc genSingleVar(p: BProc, a: PNode) = proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty + var v: TLoc + initLocExpr(p, a.sons[0], v) + genLineDir(p, a) if immediateAsgn: - var v: TLoc - initLocExpr(p, a.sons[0], v) - genLineDir(p, a) loadInto(p, a.sons[0], a.sons[2], v) + else: + constructLoc(p, v) proc genVarStmt(p: BProc, n: PNode) = for it in n.sons: @@ -609,7 +611,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "throw $1;$n", [e]) else: lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n", - [e, makeCString(typ.sym.name.s), + [e, makeCString(typ.sym.name.s), makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s), makeCString(toFileName(p.config, t.info)), rope(toLinenumber(t.info))]) else: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bbfd72354..266f63647 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -193,8 +193,6 @@ proc isImportedCppType(t: PType): bool = (x.sym != nil and sfInfixCall in x.sym.flags) proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope -proc needsComplexAssignment(typ: PType): bool = - result = containsGarbageCollectedRef(typ) proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and @@ -214,7 +212,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = of ctStruct: let t = skipTypes(rettype, typedescInst) if rettype.isImportedCppType or t.isImportedCppType: return false - result = needsComplexAssignment(t) or + result = containsGarbageCollectedRef(t) or (t.kind == tyObject and not isObjLackingTypeField(t)) else: result = false diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 071cd6446..3545edc88 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -294,10 +294,10 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, type TAssignmentFlag = enum - needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil + needToCopy TAssignmentFlags = set[TAssignmentFlag] -proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) +proc genRefAssign(p: BProc, dest, src: TLoc) proc isComplexValueType(t: PType): bool {.inline.} = let t = t.skipTypes(abstractInst + tyUserTypeClasses) @@ -313,7 +313,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = var nilLoc: TLoc initLoc(nilLoc, locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") - genRefAssign(p, loc, nilLoc, {afSrcIsNil}) + genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: @@ -1490,7 +1490,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = result = true if optForceFullMake notin m.config.globalOptions: if not equalsFile(code, cfile.cname): - if isDefined(m.config, "nimdiff"): + if m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"): if fileExists(cfile.cname): copyFile(cfile.cname.string, cfile.cname.string & ".backup") echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index c2d908193..5ded6d054 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1009,7 +1009,7 @@ proc stateFromGotoState(n: PNode): int = assert(n.kind == nkGotoState) result = n[0].intVal.int -proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = +proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode = # This transforms 3 patterns: ########################## 1 # yield e @@ -1051,7 +1051,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = result.add(retStmt) else: for i in 0 ..< n.len: - n[i] = ctx.tranformStateAssignments(n[i]) + n[i] = ctx.transformStateAssignments(n[i]) of nkSkip: discard @@ -1071,7 +1071,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = else: for i in 0 ..< n.len: - n[i] = ctx.tranformStateAssignments(n[i]) + n[i] = ctx.transformStateAssignments(n[i]) proc skipStmtList(ctx: Ctx; n: PNode): PNode = result = n @@ -1220,18 +1220,20 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = # while true: # block :stateLoop: # gotoState :state + # local vars decl (if needed) # body # Might get wrapped in try-except let loopBody = newNodeI(nkStmtList, n.info) result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody) result.info = n.info + let localVars = newNodeI(nkStmtList, n.info) if not ctx.stateVarSym.isNil: let varSect = newNodeI(nkVarSection, n.info) addVar(varSect, newSymNode(ctx.stateVarSym)) - loopBody.add(varSect) + localVars.add(varSect) if not ctx.tempVars.isNil: - loopBody.add(ctx.tempVars) + localVars.add(ctx.tempVars) let blockStmt = newNodeI(nkBlockStmt, n.info) blockStmt.add(newSymNode(ctx.stateLoopLabel)) @@ -1240,7 +1242,7 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = gs.add(ctx.newStateAccess()) gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1)) - var blockBody = newTree(nkStmtList, gs, n) + var blockBody = newTree(nkStmtList, gs, localVars, n) if ctx.hasExceptions: blockBody = ctx.wrapIntoTryExcept(blockBody) @@ -1292,7 +1294,6 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = # should folllow the same logic. ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info) ctx.stateVarSym.typ = g.createClosureIterStateType(fn) - ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) var n = n.toStmtList @@ -1320,7 +1321,7 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = result.add(s) result.add(body) - result = ctx.tranformStateAssignments(result) + result = ctx.transformStateAssignments(result) result = ctx.wrapIntoStateLoop(result) # echo "TRANSFORM TO STATES: " diff --git a/compiler/commands.nim b/compiler/commands.nim index 14141696f..fa17e9851 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -642,14 +642,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "help", "h": expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) - of "symbolfiles", "incremental": + of "symbolfiles": discard "ignore for backwards compat" + of "incremental": + when not defined(nimIncremental): + localError(conf, info, "the compiler was not built with " & + "incremental compilation features; bootstrap with " & + "-d:nimIncremental to enable") case arg.normalize of "on": conf.symbolFiles = v2Sf of "off": conf.symbolFiles = disabledSf of "writeonly": conf.symbolFiles = writeOnlySf of "readonly": conf.symbolFiles = readOnlySf of "v2": conf.symbolFiles = v2Sf - else: localError(conf, info, "invalid option for --symbolFiles: " & arg) + else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optSkipSystemConfigFile) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ab89481b8..9a4c1701c 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -86,7 +86,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimUncheckedArrayTyp") defineSymbol("nimHasTypeof") defineSymbol("nimErrorProcCanHaveBody") - + defineSymbol("nimHasInstantiationOfInMacro") defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index b621e99b9..51ad26f2c 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -117,7 +117,7 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, strutils, options, dfa, lowerings, tables, modulegraphs, - lineinfos + lineinfos, parampatterns const InterestingSyms = {skVar, skResult, skLet} @@ -261,6 +261,11 @@ proc isLastRead(n: PNode; c: var Con): bool = template interestingSym(s: PSym): bool = s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) +template isUnpackedTuple(s: PSym): bool = + ## we move out all elements of unpacked tuples, + ## hence unpacked tuples themselves don't need to be destroyed + s.kind == skTemp and s.typ.kind == tyTuple + proc patchHead(n: PNode) = if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: let s = n[0].sym @@ -397,9 +402,10 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = var m = genCopy(c, n.typ, tmp, n) m.add p(n, c) result.add m - message(c.graph.config, n.info, hintPerformance, - ("passing '$1' to a sink parameter introduces an implicit copy; " & - "use 'move($1)' to prevent it") % $n) + if isLValue(n): + message(c.graph.config, 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 @@ -446,6 +452,18 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink) #recurse(ri, ri2) result.add ri2 + of nkBracketExpr: + if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym): + # unpacking of tuple: move out the elements + result = genSink(c, dest.typ, dest, ri) + else: + result = genCopy(c, dest.typ, dest, ri) + result.add p(ri, c) + of nkStmtListExpr: + result = newNodeI(nkStmtList, ri.info) + for i in 0..ri.len-2: + result.add p(ri[i], c) + result.add moveOrCopy(dest, ri[^1], c) of nkObjConstr: result = genSink(c, dest.typ, dest, ri) let ri2 = copyTree(ri) @@ -454,6 +472,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = # so these all act like 'sink' parameters: ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) result.add ri2 + of nkTupleConstr: + result = genSink(c, dest.typ, dest, ri) + let ri2 = copyTree(ri) + for i in 0..<ri.len: + # everything that is passed to an tuple constructor is consumed, + # so these all act like 'sink' parameters: + if ri[i].kind == nkExprColonExpr: + ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) + else: + ri2[i] = pArg(ri[i], c, isSink = true) + result.add ri2 of nkSym: if ri.sym.kind != skParam and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) @@ -483,7 +512,7 @@ proc p(n: PNode; c: var Con): PNode = if it.kind == nkVarTuple and hasDestructor(ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c) - elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): + elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isUnpackedTuple(it[0].sym): for j in 0..L-2: let v = it[j] doAssert v.kind == nkSym diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6f61d020d..67f4108e1 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -400,7 +400,13 @@ proc extractImports(n: PNode; result: PNode) = for i in 0..<n.safeLen: extractImports(n[i], result) proc prepareExamples(d: PDoc; n: PNode) = + + var docComment = newTree(nkCommentStmt) + let loc = d.conf.toFileLineCol(n.info) + docComment.comment = "autogenerated by docgen from " & loc + var runnableExamples = newTree(nkStmtList, + docComment, newTree(nkImportStmt, newStrNode(nkStrLit, d.filename))) runnableExamples.info = n.info let imports = newTree(nkStmtList) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 43d5a8698..09a1cd436 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -47,7 +47,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = internalAssert c.config, sfGenSym in s.flags or s.kind == skType var x = PSym(idTableGet(c.mapping, s)) if x == nil: - x = copySym(s, false) + x = copySym(s) x.owner = c.genSymOwner idTablePut(c.mapping, s, x) result.add newSymNode(x, if c.instLines: actual.info else: templ.info) diff --git a/compiler/importer.nim b/compiler/importer.nim index 60b7872fe..131b1ad8a 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -100,7 +100,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skModule: if s.kind != skEnumField: if s.kind notin ExportableSymKinds: - internalError(c.config, s.info, "importAllSymbols: " & $s.kind) + internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 19bbde777..a9813f5c5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -66,6 +66,12 @@ type res: Rope # result part; index if this is an # (address, index)-tuple address: Rope # address of an (address, index)-tuple + tmpLoc: Rope # tmp var which stores the (address, index) + # pair to prevent multiple evals. + # the tmp is initialized upon evaling the + # address. + # might be nil. + # (see `maybeMakeTemp`) TBlock = object id: int # the ID of the label; positive means that it @@ -131,16 +137,15 @@ proc newGlobals(): PGlobals = proc initCompRes(r: var TCompRes) = r.address = nil r.res = nil + r.tmpLoc = nil r.typ = etyNone r.kind = resNone proc rdLoc(a: TCompRes): Rope {.inline.} = - result = a.res - when false: - if a.typ != etyBaseIndex: - result = a.res - else: - result = "$1[$2]" % [a.address, a.res] + if a.typ != etyBaseIndex: + result = a.res + else: + result = "$1[$2]" % [a.address, a.res] proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = @@ -447,12 +452,48 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] +proc needsTemp(p: PProc; n: PNode): bool = + # check if n contains a call to determine + # if a temp should be made to prevent multiple evals + if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}: + return true + for c in n: + if needsTemp(p, c): + return true + +proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = + var + a = x.rdLoc + b = a + if needsTemp(p, n): + # if we have tmp just use it + if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}): + b = "$1[0][$1[1]]" % [x.tmpLoc] + (a: a, tmp: b) + else: + let tmp = p.getTemp + b = tmp + a = "($1 = $2, $1)" % [tmp, a] + (a: a, tmp: b) + else: + (a: a, tmp: b) + proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = + # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr, + # if $3 or $4 are present they will be substituted with temps for + # lhs and rhs respectively var x, y: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = frmt % [x.rdLoc, y.rdLoc] + + var + a, tmp = x.rdLoc + b, tmp2 = y.rdLoc + if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) + if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) + + r.res = frmt % [a, b, tmp, tmp2] r.kind = resExpr proc unsignedTrimmerJS(size: BiggestInt): Rope = @@ -473,7 +514,8 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) if reassign: - r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] else: r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] @@ -487,9 +529,12 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.kind = resExpr proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = + # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1 useMagic(p, magic) gen(p, n.sons[1], r) - r.res = frmt % [r.rdLoc] + var a, tmp = r.rdLoc + if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r) + r.res = frmt % [a, tmp] r.kind = resExpr proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = @@ -524,6 +569,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) + of mEqRef, mEqUntracedRef: + if mapType(n[1].typ) != etyBaseIndex: + arithAux(p, n, r, op) + else: + var x, y: TCompRes + gen(p, n[1], x) + gen(p, n[2], y) + r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] else: arithAux(p, n, r, op) r.kind = resExpr @@ -801,6 +854,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = # A fat pointer is disguised as an array r.res = r.address r.address = nil + r.typ = etyNone elif r.typ == etyBaseIndex: # Deference first r.res = "$1[$2]" % [r.address, r.res] @@ -863,26 +917,42 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(p: PProc; y: PNode): bool = - result = (y.kind in nodeKindsNeedNoCopy) or - (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) + # if the node is a literal object constructor we have to recursively + # check the expressions passed into it + case y.kind + of nkObjConstr: + for arg in y.sons[1..^1]: + if not needsNoCopy(p, arg[1]): + return false + of nkTupleConstr: + for arg in y.sons: + var arg = arg + if arg.kind == nkExprColonExpr: + arg = arg[1] + if not needsNoCopy(p, arg): + return false + of nkBracket: + for arg in y.sons: + if not needsNoCopy(p, arg): + return false + of nodeKindsNeedNoCopy: + return true + else: + return (mapType(y.typ) != etyBaseIndex and + (skipTypes(y.typ, abstractInst).kind in + {tyRef, tyPtr, tyLent, tyVar, tyCString} + IntegralTypes)) + return true proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes var xtyp = mapType(p, x.typ) - if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: - gen(p, x.sons[0], a) - let tmp = p.getTemp(false) - lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc]) - a.res = "$1[0][$1[1]]" % [tmp] - else: - gen(p, x, a) - + gen(p, x, a) genLineDir(p, y) gen(p, y, b) @@ -911,13 +981,13 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = let tmp = p.getTemp(false) lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) elif b.typ == etyBaseIndex: - lineF(p, "$# = $#;$n", [a.res, b.rdLoc]) + lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) else: internalError(p.config, x.info, "genAsgn") else: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: - lineF(p, "$1 = $2;$n", [a.res, b.res]) + lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) proc genAsgn(p: PProc, n: PNode) = genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) @@ -971,17 +1041,30 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = - r.typ = etyNone gen(p, n.sons[0], r) + r.typ = mapType(n.typ) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) + + template mkTemp(i: int) = + if r.typ == etyBaseIndex: + if needsTemp(p, n[i]): + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.res] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + else: + r.address = "$1[0]" % [r.res] + r.res = "$1[1]" % [r.res] if otyp.kind == tyTuple: r.res = ("$1.Field$2") % [r.res, getFieldPosition(p, n.sons[1]).rope] + mkTemp(0) else: if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = "$1.$2" % [r.res, f.loc.r] + mkTemp(1) r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) @@ -1039,14 +1122,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) - internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex - r.address = a.res + #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex + let (x, tmp) = maybeMakeTemp(p, m[0], a) + r.address = x var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0]) else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") - r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), tmp] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: @@ -1062,13 +1146,22 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = of tyTuple: genFieldAddr(p, n, r) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') - r.typ = etyNone + r.typ = mapType(n.typ) if r.res == nil: internalError(p.config, n.info, "genArrayAccess") if ty.kind == tyCString: r.res = "$1.charCodeAt($2)" % [r.address, r.res] + elif r.typ == etyBaseIndex: + if needsTemp(p, n[0]): + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + else: + let x = r.rdLoc + r.address = "$1[0]" % [x] + r.res = "$1[1]" % [x] else: r.res = "$1[$2]" % [r.address, r.res] - r.address = nil r.kind = resExpr template isIndirect(x: PSym): bool = @@ -1169,8 +1262,12 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] + if isIndirect(s): + r.address = "$1[0][0]" % [s.loc.r] + r.res = "$1[0][1]" % [s.loc.r] + else: + r.address = "$1[0]" % [s.loc.r] + r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r r.res = s.loc.r & "_Idx" @@ -1210,14 +1307,17 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = else: var a: TCompRes gen(p, it, a) - r.kind = resExpr - if a.typ == etyBaseIndex: - r.res = "$1[$2]" % [a.address, a.res] - elif it.kind == nkCall: + r.kind = a.kind + r.typ = mapType(p, n.typ) + if r.typ == etyBaseIndex: let tmp = p.getTemp - r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res] - elif t == etyBaseIndex: - r.res = "$1[0]" % [a.res] + r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + elif a.typ == etyBaseIndex: + if a.tmpLoc != nil: + r.tmpLoc = a.tmpLoc + r.res = a.rdLoc else: internalError(p.config, n.info, "genDeref") @@ -1242,7 +1342,7 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = add(r.res, ", ") add(r.res, a.res) if emitted != nil: inc emitted[] - elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: + elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: # this fixes bug #5608: let tmp = getTemp(p) add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) @@ -1366,6 +1466,14 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) = return gen(p, n.sons[0], r) genArgs(p, n, r) + if n.typ != nil: + let t = mapType(n.typ) + if t == etyBaseIndex: + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + r.typ = t proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let n = n[1].skipConv @@ -1472,12 +1580,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = createObjInitList(p, t, initIntSet(), initList) result = ("{$1}") % [initList] if indirect: result = "[$1]" % [result] - of tyVar, tyPtr, tyLent, tyRef: + of tyVar, tyPtr, tyLent, tyRef, tyPointer: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) - of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc: + of tySequence, tyOpt, tyString, tyCString, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: @@ -1511,10 +1619,13 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = varCode = v.constraint.strVal if n.kind == nkEmpty: - lineF(p, varCode & " = $3;$n", - [returnType, varName, createVar(p, v.typ, isIndirect(v))]) - if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: + if not isIndirect(v) and + v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: + lineF(p, "var $1 = null;$n", [varName]) lineF(p, "var $1_Idx = 0;$n", [varName]) + else: + lineF(p, varCode & " = $3;$n", + [returnType, varName, createVar(p, v.typ, isIndirect(v))]) else: gen(p, n, a) case mapType(p, v.typ) @@ -1531,8 +1642,12 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = lineF(p, varCode & " = $3, $2_Idx = $4;$n", [returnType, v.loc.r, a.address, a.res]) else: - lineF(p, varCode & " = [$3, $4];$n", - [returnType, v.loc.r, a.address, a.res]) + if isIndirect(v): + lineF(p, varCode & " = [[$3, $4]];$n", + [returnType, v.loc.r, a.address, a.res]) + else: + lineF(p, varCode & " = [$3, $4];$n", + [returnType, v.loc.r, a.address, a.res]) else: if targetBaseIndex: let tmp = p.getTemp @@ -1579,7 +1694,12 @@ proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) + if mapType(t) == etyObject: + lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)]) + elif a.typ == etyBaseIndex: + lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)]) + else: + lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes @@ -1603,20 +1723,20 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: - r.res.add("($1).concat(" % [a.res]) + r.res.add("($1 || []).concat(" % [a.res]) for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: - r.res.add("$1," % [a.res]) + r.res.add("$1 || []," % [a.res]) gen(p, n.sons[sonsLen(n) - 1], a) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1])" % [a.res]) else: - r.res.add("$1)" % [a.res]) + r.res.add("$1 || [])" % [a.res]) proc genToArray(p: PProc; n: PNode; r: var TCompRes) = # we map mArray to PHP's array constructor, a mild hack: @@ -1701,8 +1821,12 @@ proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) - addf(p.body, "$1 = genericReset($1, $2);$n", [x.res, - genTypeInfo(p, n.sons[1].typ)]) + if x.typ == etyBaseIndex: + lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) + else: + let (a, tmp) = maybeMakeTemp(p, n[1], x) + lineF(p, "$1 = genericReset($3, $2);$n", [a, + genTypeInfo(p, n.sons[1].typ), tmp]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var @@ -1721,32 +1845,37 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: binaryExpr(p, n, r, "addChar", - "if ($1 != null) { addChar($1, $2); } else { $1 = [$2]; }") + "if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }") of mAppendStrStr: var lhs, rhs: TCompRes gen(p, n[1], lhs) gen(p, n[2], rhs) let rhsIsLit = n[2].kind in nkStrKinds + let (a, tmp) = maybeMakeTemp(p, n[1], lhs) if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - r.res = "if ($1 != null) { $1 += $2; } else { $1 = $2$3; }" % [ - lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"] + r.res = "if ($1 != null) { $4 += $2; } else { $4 = $2$3; }" % [ + a, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] else: - r.res = "if ($1 != null) { $1 = ($1).concat($2); } else { $1 = $2$3; }" % [ - lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"] + r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [ + lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] r.kind = resExpr of mAppendSeqElem: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) - if needsNoCopy(p, n[2]): - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + if mapType(n[2].typ) == etyBaseIndex: + let c = "[$1, $2]" % [y.address, y.res] + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] + elif needsNoCopy(p, n[2]): + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp] else: useMagic(p, "nimCopy") let c = getTemp(p, defineInLocals=false) lineF(p, "var $1 = nimCopy(null, $2, $3);$n", [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] r.kind = resExpr of mConStrStr: genConStrStr(p, n, r) @@ -1756,38 +1885,56 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") - of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") + of mIsNil: + if mapType(n[1].typ) != etyBaseIndex: + unaryExpr(p, n, r, "", "($1 === null)") + else: + var x: TCompRes + gen(p, n[1], x) + r.res = "($# === null && $# === 0)" % [x.address, x.res] of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) - of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do + of mChr: gen(p, n.sons[1], r) + of mArrToSeq: + if needsNoCopy(p, n.sons[1]): + gen(p, n.sons[1], r) + else: + var x: TCompRes + gen(p, n.sons[1], x) + useMagic(p, "nimCopy") + r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] + of mDestroy: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: - unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") + unaryExpr(p, n, r, "", "($1 != null ? $2.length : 0)") of mXLenStr, mXLenSeq: unaryExpr(p, n, r, "", "$1.length") of mHigh: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") + unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "+", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") - else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)") + else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)") of ast.mDec: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "-", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") - else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") + else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)") of mSetLengthStr: - binaryExpr(p, n, r, "", "$1.length = $2") + binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); } - else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + let (b, tmp2) = maybeMakeTemp(p, n[2], y) + r.res = """if ($1 === null) $4 = []; + if ($4.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } + else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") @@ -1855,7 +2002,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) - add(r.res, a.res) + if a.typ == etyBaseIndex: + addf(r.res, "[$1, $2]", [a.address, a.res]) + else: + add(r.res, a.res) add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = @@ -1867,7 +2017,10 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - addf(r.res, "Field$#: $#", [i.rope, a.res]) + if a.typ == etyBaseIndex: + addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res]) + else: + addf(r.res, "Field$#: $#", [i.rope, a.res]) r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = @@ -1887,12 +2040,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let typ = val.typ.skipTypes(abstractInst) if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and - mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]): + mapType(p, typ) != etyBaseIndex) or + a.typ == etyBaseIndex or + needsNoCopy(p, it.sons[1]): discard else: useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - addf(initList, "$#: $#", [f.loc.r, a.res]) + if a.typ == etyBaseIndex: + addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res]) + else: + addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] @@ -2010,11 +2168,14 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym let mname = mangleName(p.module, resultSym) - let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) - resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) - if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and + if not isindirect(resultSym) and + resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, resultSym.typ) == etyBaseIndex: + resultAsgn = p.indentLine(("var $# = null;$n") % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) + else: + let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) + resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) gen(p, prc.ast.sons[resultPos], a) if mapType(p, resultSym.typ) == etyBaseIndex: returnStmt = "return [$#, $#];$n" % [a.address, a.res] @@ -2106,6 +2267,13 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = of 4: "0xfffffffe" else: "" r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] + elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: + r.address = r.res + r.res = ~"null" + r.typ = etyBaseIndex + elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer: + r.res = r.address + r.typ = etyObject proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c318421fa..874cb4bd0 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -258,7 +258,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = # add 'new' statement: result.add newCall(getSysSym(g, n.info, "internalNew"), env) result.add makeClosure(g, iter, env, n.info) - + proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = let envParam = getHiddenParam(g, owner) let obj = envParam.typ.lastSon @@ -454,11 +454,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = createUpField(c, w, up, n.info) w = up of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, - nkTemplateDef, nkTypeSection: - discard - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: + nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, + nkConverterDef, nkMacroDef, nkFuncDef: discard - of nkLambdaKinds, nkIteratorDef, nkFuncDef: + of nkLambdaKinds, nkIteratorDef: if n.typ != nil: detectCapturedVars(n[namePos], owner, c) of nkReturnStmt: @@ -672,9 +671,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; else: result = accessViaEnvVar(n, owner, d, c) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, - nkTemplateDef, nkTypeSection: - discard - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: + nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef, + nkMacroDef, nkFuncDef: discard of nkClosure: if n[1].kind == nkNilLit: @@ -685,7 +683,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # now we know better, so patch it: n.sons[0] = x.sons[0] n.sons[1] = x.sons[1] - of nkLambdaKinds, nkIteratorDef, nkFuncDef: + of nkLambdaKinds, nkIteratorDef: if n.typ != nil and n[namePos].kind == nkSym: let oldInContainer = c.inContainer c.inContainer = 0 @@ -720,19 +718,37 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # ------------------ old stuff ------------------------------------------- proc semCaptureSym*(s, owner: PSym) = + discard """ + proc outer() = + var x: int + proc inner() = + proc innerInner() = + echo x + innerInner() + inner() + # inner() takes a closure too! + """ + proc propagateClosure(start, last: PSym) = + var o = start + while o != nil and o.kind != skModule: + if o == last: break + o.typ.callConv = ccClosure + o = o.skipGenericOwner + if interestingVar(s) and s.kind != skResult: if owner.typ != nil and not isGenericRoutine(owner): # XXX: is this really safe? # if we capture a var from another generic routine, # it won't be consider captured. var o = owner.skipGenericOwner - while o.kind != skModule and o != nil: + while o != nil and o.kind != skModule: if s.owner == o: if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator: owner.typ.callConv = ccClosure + propagateClosure(owner.skipGenericOwner, s.owner) else: discard "do not produce an error here, but later" - #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s + #echo "computing .closure for ", owner.name.s, " because of ", s.name.s o = o.skipGenericOwner # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # here diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d2e7fdcfa..2fb4e5241 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -168,7 +168,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(c.config, s)) inc missingImpls - elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: + elif {sfUsed, sfExported} * s.flags == {}: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: # XXX: implicit type params are currently skTypes # maybe they can be made skGenericParam as well. diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 603def326..d199abcc7 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -78,7 +78,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options) + var temp = newSym(skTemp, getIdent(g.cache, "_"), owner, value.info, owner.options) var v = newNodeI(nkLetSection, value.info) let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) diff --git a/compiler/modules.nim b/compiler/modules.nim index e2f322561..442305a06 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -17,18 +17,7 @@ import proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) -proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = - # We cannot call ``newSym`` here, because we have to circumvent the ID - # mechanism, which we do in order to assign each module a persistent ID. - new(result) - result.id = -1 # for better error checking - result.kind = skModule - let filename = toFullPath(graph.config, fileIdx) - result.name = getIdent(graph.cache, splitFile(filename).name) - if not isNimIdentifier(result.name.s): - rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) - - result.info = newLineInfo(fileIdx, 1, 1) +proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) = let pck = getPackageName(graph.config, filename) pck2 = if pck.len > 0: pck else: "unknown" @@ -38,13 +27,11 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) - result.owner = packSym result.position = int fileIdx if int(fileIdx) >= graph.modules.len: setLen(graph.modules, int(fileIdx) + 1) - #growCache graph.modules, int fileIdx graph.modules[result.position] = result incl(result.flags, sfUsed) @@ -58,16 +45,36 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # strTableIncl() for error corrections: discard strTableIncl(packSym.tab, result) +proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = + # We cannot call ``newSym`` here, because we have to circumvent the ID + # mechanism, which we do in order to assign each module a persistent ID. + new(result) + result.id = -1 # for better error checking + result.kind = skModule + let filename = toFullPath(graph.config, fileIdx) + result.name = getIdent(graph.cache, splitFile(filename).name) + if not isNimIdentifier(result.name.s): + rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) + result.info = newLineInfo(fileIdx, 1, 1) + partialInitModule(result, graph, fileIdx, filename) + proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: - result = newModule(graph, fileIdx) - result.flags = result.flags + flags - if sfMainModule in result.flags: - graph.config.mainPackageId = result.owner.id - - result.id = getModuleId(graph, fileIdx, AbsoluteFile toFullPath(graph.config, fileIdx)) - registerModule(graph, result) + let filename = toFullPath(graph.config, fileIdx) + let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename) + result = r + if result == nil: + result = newModule(graph, fileIdx) + result.flags = result.flags + flags + if sfMainModule in result.flags: + graph.config.mainPackageId = result.owner.id + result.id = id + registerModule(graph, result) + else: + partialInitModule(result, graph, fileIdx, filename) + result.id = id + assert result.id < 0 discard processModule(graph, result, if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) elif graph.isDirty(result): diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 4ac5a839d..7e6b67cbe 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -190,7 +190,7 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = if info.fileIndex.int32 < 0: result = "???" - return + return let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string if optListFullPaths in conf.globalOptions: diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 4873f90d6..703467bc4 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -73,23 +73,6 @@ iterator dirs(x: string): (int, int) = var it: PathIter while hasNext(it, x): yield next(it, x) -when false: - iterator dirs(x: string): (int, int) = - var i = 0 - var first = true - while i < x.len: - let prev = i - if first and x[i] in {DirSep, AltSep}: - # absolute path: - inc i - else: - while i < x.len and x[i] notin {DirSep, AltSep}: inc i - if i > prev: - yield (prev, i-1) - first = false - # skip all separators: - while i < x.len and x[i] in {DirSep, AltSep}: inc i - proc isDot(x: string; bounds: (int, int)): bool = bounds[1] == bounds[0] and x[bounds[0]] == '.' diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index ef5223559..3d8e5645b 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -38,7 +38,7 @@ const wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wTags, wLocks, wGcSafe, wExportNims, wUsed} - exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} + exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNosideeffect} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, wWarnings, wHints, @@ -855,8 +855,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, sym.flags.incl sfOverriden of wNosideeffect: noVal(c, it) - incl(sym.flags, sfNoSideEffect) - if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) + if sym != nil: + incl(sym.flags, sfNoSideEffect) + if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) of wSideeffect: noVal(c, it) incl(sym.flags, sfSideEffect) @@ -1110,10 +1111,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.flags.incl sfUsed of wLiftLocals: discard else: invalidPragma(c, it) - elif sym.kind in {skVar,skLet,skParam,skField,skProc,skFunc,skConverter,skMethod,skType}: + elif sym != nil and sym.kind in {skVar, skLet, skParam, skField, skProc, + skFunc, skConverter, skMethod, skType}: n.sons[i] = semCustomPragma(c, it) - else: + elif sym != nil: illegalCustomPragma(c, it, sym) + else: + invalidPragma(c, it) + +proc mergePragmas(n, pragmas: PNode) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = pragmas + else: + for p in pragmas: n.sons[pragmasPos].add p proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = @@ -1123,11 +1133,12 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, if not o.isNil: pushInfoContext(c.config, n.info) var i = 0 - while i < o.len(): + while i < o.len: if singlePragma(c, sym, o, i, validPragmas): internalError(c.config, n.info, "implicitPragmas") inc i popInfoContext(c.config) + if sym.kind in routineKinds: mergePragmas(sym.ast, o) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, n.info, ".dynlib requires .exportc") diff --git a/compiler/rod.nim b/compiler/rod.nim index 92489ffdd..bbd2f0c6c 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -16,7 +16,7 @@ when not nimIncremental: template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList) - template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = getID() + proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) {.inline.} = (nil, getID()) template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index c61c4ba04..147e8c3d6 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -52,7 +52,7 @@ proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile; return true return false -proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = +proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = ## Analyse the known dependency graph. if g.config.symbolFiles == disabledSf: return getID() when false: @@ -82,8 +82,12 @@ proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): i db.exec(sql"delete from toplevelstmts where module = ?", module[0]) db.exec(sql"delete from statics where module = ?", module[0]) +proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) = + let id = getModuleId(g, fileIdx, fullpath) + result = (g.incr.r.syms.getOrDefault(abs id), id) + proc pushType(w: var Writer, t: PType) = - if not containsOrIncl(w.tmarks, t.id): + if not containsOrIncl(w.tmarks, t.uniqueId): w.tstack.add(t) proc pushSym(w: var Writer, s: PSym) = @@ -109,7 +113,8 @@ proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, result.add(',') encodeVInt(int n.info.line, result) result.add(',') - encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result) + #encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result) + encodeVInt(n.info.fileIndex.int, result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) @@ -126,7 +131,7 @@ proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, encodeVInt(cast[int32](f), result) if n.typ != nil: result.add('^') - encodeVInt(n.typ.id, result) + encodeVInt(n.typ.uniqueId, result) pushType(w, n.typ) case n.kind of nkCharLit..nkUInt64Lit: @@ -187,7 +192,10 @@ proc encodeType(g: ModuleGraph, t: PType, result: var string) = add(result, '[') encodeVInt(ord(t.kind), result) add(result, '+') - encodeVInt(t.id, result) + encodeVInt(t.uniqueId, result) + if t.id != t.uniqueId: + add(result, '+') + encodeVInt(t.id, result) if t.n != nil: encodeNode(g, unknownLineInfo(), t.n, result) if t.flags != {}: @@ -236,12 +244,16 @@ proc encodeType(g: ModuleGraph, t: PType, result: var string) = encodeVInt(s.id, result) pushSym(w, s) encodeLoc(g, t.loc, result) + if t.typeInst != nil: + add(result, '\21') + encodeVInt(t.typeInst.uniqueId, result) + pushType(w, t.typeInst) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: add(result, "^()") else: add(result, '^') - encodeVInt(t.sons[i].id, result) + encodeVInt(t.sons[i].uniqueId, result) pushType(w, t.sons[i]) proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) = @@ -260,7 +272,7 @@ proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation]; pushSym(w, t.sym) for tt in t.concreteTypes: result.add('\17') - encodeVInt(tt.id, result) + encodeVInt(tt.uniqueId, result) pushType(w, tt) result.add('\20') encodeVInt(t.compilesId, result) @@ -278,14 +290,15 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = encodeStr(s.name.s, result) if s.typ != nil: result.add('^') - encodeVInt(s.typ.id, result) + encodeVInt(s.typ.uniqueId, result) pushType(w, s.typ) result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') encodeVInt(int s.info.line, result) result.add(',') - encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result) + #encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result) + encodeVInt(s.info.fileIndex.int, result) if s.owner != nil: result.add('*') encodeVInt(s.owner.id, result) @@ -313,7 +326,7 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = of skType, skGenericParam: for t in s.typeInstCache: result.add('\14') - encodeVInt(t.id, result) + encodeVInt(t.uniqueId, result) pushType(w, t) of routineKinds: encodeInstantiations(g, s.procInstCache, result) @@ -321,6 +334,9 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = result.add('\16') encodeVInt(s.gcUnsafetyReason.id, result) pushSym(w, s.gcUnsafetyReason) + if s.transformedBody != nil: + result.add('\24') + encodeNode(g, s.info, s.transformedBody, result) of skModule, skPackage: encodeInstantiations(g, s.usedGenerics, result) # we don't serialize: @@ -361,18 +377,12 @@ proc storeType(g: ModuleGraph; t: PType) = let m = if t.owner != nil: getModule(t.owner) else: nil let mid = if m == nil: 0 else: abs(m.id) db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", - t.id, mid, buf) + t.uniqueId, mid, buf) -proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if g.config.symbolFiles == disabledSf: return - var buf = newStringOfCap(160) - encodeNode(g, module.info, n, buf) - db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", - abs(module.id), module.offset, buf) - inc module.offset +proc transitiveClosure(g: ModuleGraph) = var i = 0 while true: - if i > 10_000: + if i > 100_000: doAssert false, "loop never ends!" if w.sstack.len > 0: let s = w.sstack.pop() @@ -383,14 +393,30 @@ proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = let t = w.tstack.pop() storeType(g, t) when false: - echo "popped type ", typeToString(t), " ", t.id + echo "popped type ", typeToString(t), " ", t.uniqueId else: break inc i +proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = + if g.config.symbolFiles == disabledSf: return + var buf = newStringOfCap(160) + encodeNode(g, module.info, n, buf) + db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", + abs(module.id), module.offset, buf) + inc module.offset + transitiveClosure(g) + proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) = storeNode(g, module, n) +proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) = + let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string) + if id.len == 0: + let fullhash = hashFileCached(g.config, fileIdx, fullpath) + db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", + int(fileIdx), fullpath.string, fullhash) + proc storeRemaining*(g: ModuleGraph; module: PSym) = if g.config.symbolFiles == disabledSf: return var stillForwarded: seq[PSym] = @[] @@ -400,6 +426,13 @@ proc storeRemaining*(g: ModuleGraph; module: PSym) = else: stillForwarded.add s swap w.forwardedSyms, stillForwarded + transitiveClosure(g) + var nimid = 0 + for x in items(g.config.m.fileInfos): + # don't store the "command line" entry: + if nimid != 0: + storeFilename(g, x.fullPath, FileIndex(nimid)) + inc nimid # ---------------- decoder ----------------------------------- @@ -426,9 +459,11 @@ proc decodeLineInfo(g; b; info: var TLineInfo) = else: info.line = uint16(decodeVInt(b.s, b.pos)) if b.s[b.pos] == ',': inc(b.pos) - info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) + #info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) + info.fileIndex = FileIndex decodeVInt(b.s, b.pos) proc skipNode(b) = + # ')' itself cannot be part of a string literal so that this is correct. assert b.s[b.pos] == '(' var par = 0 var pos = b.pos+1 @@ -563,13 +598,18 @@ proc loadType(g; id: int; info: TLineInfo): PType = result.kind = TTypeKind(decodeVInt(b.s, b.pos)) if b.s[b.pos] == '+': inc(b.pos) - result.id = decodeVInt(b.s, b.pos) - setId(result.id) + result.uniqueId = decodeVInt(b.s, b.pos) + setId(result.uniqueId) #if debugIds: registerID(result) else: internalError(g.config, info, "decodeType: no id") + if b.s[b.pos] == '+': + inc(b.pos) + result.id = decodeVInt(b.s, b.pos) + else: + result.id = result.uniqueId # here this also avoids endless recursion for recursive type - g.incr.r.types.add(result.id, result) + g.incr.r.types.add(result.uniqueId, result) if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo()) if b.s[b.pos] == '$': inc(b.pos) @@ -620,6 +660,10 @@ proc loadType(g; id: int; info: TLineInfo): PType = let y = loadSym(g, decodeVInt(b.s, b.pos), info) result.methods.add((x, y)) decodeLoc(g, b, result.loc, info) + if b.s[b.pos] == '\21': + inc(b.pos) + let d = decodeVInt(b.s, b.pos) + result.typeInst = loadType(g, d, info) while b.s[b.pos] == '^': inc(b.pos) if b.s[b.pos] == '(': @@ -725,6 +769,9 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym = if b.s[b.pos] == '\16': inc(b.pos) result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info) + if b.s[b.pos] == '\24': + inc b.pos + result.transformedBody = decodeNode(g, b, result.info) of skModule, skPackage: decodeInstantiations(g, b, result.info, result.usedGenerics) of skLet, skVar, skField, skForVar: @@ -874,20 +921,22 @@ proc setupModuleCache*(g: ModuleGraph) = if g.config.symbolFiles == writeOnlySf: removeFile(dbfile) createDir getNimcacheDir(g.config) + let ec = encodeConfig(g) if not fileExists(dbfile): db = open(connection=string dbfile, user="nim", password="", database="nim") createDb(db) - db.exec(sql"insert into config(config) values (?)", encodeConfig(g)) + db.exec(sql"insert into config(config) values (?)", ec) else: db = open(connection=string dbfile, user="nim", password="", database="nim") let oldConfig = db.getValue(sql"select config from config") - g.incr.configChanged = oldConfig != encodeConfig(g) + g.incr.configChanged = oldConfig != ec # ensure the filename IDs stay consistent: for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"): let id = fileInfoIdx(g.config, AbsoluteFile row[0]) doAssert id.int == parseInt(row[1]) + db.exec(sql"update config set config = ?", ec) db.exec(sql"pragma journal_mode=off") # This MUST be turned off, otherwise it's way too slow even for testing purposes: db.exec(sql"pragma SYNCHRONOUS=off") diff --git a/compiler/sem.nim b/compiler/sem.nim index 775c9f7c9..924e53b66 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -204,7 +204,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = if n.kind == nkSym: # and sfGenSym in n.sym.flags: result = n.sym - if result.kind != kind: + if result.kind notin {kind, skTemp}: localError(c.config, n.info, "cannot use symbol of kind '" & $result.kind & "' as a '" & $kind & "'") if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9433a7327..08917cb29 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1490,6 +1490,13 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): untyped = typ.isMetaType and typ.kind != tyTypeDesc + +proc goodLineInfo(arg: PNode): TLineinfo = + if arg.kind == nkStmtListExpr and arg.len > 0: + goodLineInfo(arg[^1]) + else: + arg.info + proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2, c.config) var a = n.sons[0] @@ -1571,7 +1578,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = else: typeMismatch(c.config, n.info, lhs.typ, rhsTyp) - n.sons[1] = fitNode(c, le, rhs, n.info) + n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) liftTypeBoundOps(c, lhs.typ, lhs.info) #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) @@ -1968,6 +1975,22 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info +proc semSizeof(c: PContext, n: PNode): PNode = + if sonsLen(n) != 2: + localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof") + else: + n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) + #restoreOldStyleType(n.sons[1]) + n.typ = getSysType(c.graph, n.info, tyInt) + + let size = getSize(c.config, n[1].typ) + if size >= 0: + result = newIntNode(nkIntLit, size) + result.info = n.info + result.typ = n.typ + else: + result = n + proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! result = n @@ -2039,6 +2062,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mRunnableExamples: if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: when false: + # some of this dead code was moved to `prepareExamples` if sfMainModule in c.module.flags: let inp = toFullPath(c.config, c.module.info) if c.runnableExamples == nil: @@ -2052,6 +2076,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = setMs(n, s) else: result = c.graph.emptyNode + of mSizeOf: result = semSizeof(c, setMs(n, s)) of mOmpParFor: checkMinSonsLen(n, 3, c.config) result = semDirectOp(c, n, flags) @@ -2346,7 +2371,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - result = semSym(c, n, s, flags) if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) @@ -2354,6 +2378,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = markIndirect(c, result.sym) # if isGenericRoutine(result.sym): # localError(c.config, n.info, errInstantiateXExplicitly, s.name.s) + else: + result = semSym(c, n, s, flags) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! diff --git a/compiler/seminst.nim b/compiler/seminst.nim index de2e10a9b..17f61c7dd 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -118,7 +118,7 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = n.sym = x elif s.owner.kind == skPackage: #echo "copied this ", s.name.s - x = copySym(s, false) + x = copySym(s) x.owner = owner idTablePut(symMap, s, x) n.sym = x @@ -337,7 +337,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent - result = copySym(fn, false) + result = copySym(fn) incl(result.flags, sfFromGeneric) result.owner = fn result.ast = n diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 2aae562f9..df2c084a1 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -346,7 +346,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result.info = n.info result.typ = n.typ else: - localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely") + localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely, type: " & n[1].typ.typeToString) result = n of mAlignOf: result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ)) @@ -381,7 +381,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[0].sym.name.s == "=": result = semAsgnOpr(c, n) else: - result = n + result = semShallowCopy(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0317fd8ba..75dea069f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -56,6 +56,7 @@ type guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool + inEnforcedNoSideEffects: bool maxLockLevel, currLockLevel: TLockLevel config: ConfigRef graph: ModuleGraph @@ -194,10 +195,10 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = when true: template markSideEffect(a: PEffects; reason: typed) = - a.hasSideEffect = true + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true else: template markSideEffect(a: PEffects; reason: typed) = - a.hasSideEffect = true + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true markGcUnsafe(a, reason) proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = @@ -846,15 +847,20 @@ proc track(tracked: PEffects, n: PNode) = let oldLocked = tracked.locked.len let oldLockLevel = tracked.currLockLevel var enforcedGcSafety = false + var enforceNoSideEffects = false for i in 0 ..< pragmaList.len: let pragma = whichPragma(pragmaList.sons[i]) if pragma == wLocks: lockLocations(tracked, pragmaList.sons[i]) elif pragma == wGcSafe: enforcedGcSafety = true + elif pragma == wNosideeffect: + enforceNoSideEffects = true if enforcedGcSafety: tracked.inEnforcedGcSafe = true + if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true track(tracked, n.lastSon) if enforcedGcSafety: tracked.inEnforcedGcSafe = false + if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false setLen(tracked.locked, oldLocked) tracked.currLockLevel = oldLockLevel of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 1f2b9f0b3..48aa75528 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1268,7 +1268,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; x.add(prc) # recursion assures that this works for multiple macro annotations too: - var r = semOverloadedCall(c, x, x, {skMacro}, {efNoUndeclared}) + var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared}) if r == nil: # Restore the old list of pragmas since we couldn't process this prc.sons[pragmasPos] = n @@ -1860,7 +1860,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = for i in 0 ..< pragmaList.len: case whichPragma(pragmaList.sons[i]) of wLine: setLine(result, pragmaList.sons[i].info) - of wLocks, wGcSafe: + of wLocks, wGcSafe, wNosideeffect: result = n result.typ = n.sons[1].typ of wNoRewrite: @@ -1963,7 +1963,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, nkBlockStmt, nkState: discard - else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'") + else: localError(c.config, n.sons[j].info, + "unreachable statement after 'return' statement or '{.noReturn.}' proc") else: discard if result.len == 1 and diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f4a1b0302..a011a8fc8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -392,7 +392,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = localError(c.config, n.info, errTypeExpected) return errorSym(c, n) result = result.typ.sym.copySym - result.typ = copyType(result.typ, result.typ.owner, true) + result.typ = exactReplica(result.typ) result.typ.flags.incl tfUnresolved if result.kind == skGenericParam: @@ -1728,7 +1728,7 @@ proc processMagicType(c: PContext, m: PSym) = of mBool: setMagicType(c.config, m, tyBool, 1) of mChar: setMagicType(c.config, m, tyChar, 1) of mString: - setMagicType(c.config, m, tyString, c.config.target.ptrSize) + setMagicType(c.config, m, tyString, szUncomputedSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) when false: if c.config.selectedGc == gcDestructors: @@ -1758,28 +1758,28 @@ proc processMagicType(c: PContext, m: PSym) = of mVoidType: setMagicType(c.config, m, tyVoid, 0) of mArray: - setMagicType(c.config, m, tyArray, 0) + setMagicType(c.config, m, tyArray, szUncomputedSize) of mOpenArray: - setMagicType(c.config, m, tyOpenArray, 0) + setMagicType(c.config, m, tyOpenArray, szUncomputedSize) of mVarargs: - setMagicType(c.config, m, tyVarargs, 0) + setMagicType(c.config, m, tyVarargs, szUncomputedSize) of mRange: - setMagicType(c.config, m, tyRange, 0) + setMagicType(c.config, m, tyRange, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mSet: - setMagicType(c.config, m, tySet, 0) + setMagicType(c.config, m, tySet, szUncomputedSize) of mUncheckedArray: - setMagicType(c.config, m, tyUncheckedArray, 0) + setMagicType(c.config, m, tyUncheckedArray, szUncomputedSize) of mSeq: - setMagicType(c.config, m, tySequence, 0) + setMagicType(c.config, m, tySequence, szUncomputedSize) if c.config.selectedGc == gcDestructors: incl m.typ.flags, tfHasAsgn assert c.graph.sysTypes[tySequence] == nil c.graph.sysTypes[tySequence] = m.typ of mOpt: - setMagicType(c.config, m, tyOpt, 0) + setMagicType(c.config, m, tyOpt, szUncomputedSize) of mOrdinal: - setMagicType(c.config, m, tyOrdinal, 0) + setMagicType(c.config, m, tyOrdinal, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime @@ -1787,7 +1787,7 @@ proc processMagicType(c: PContext, m: PSym) = of mBuiltinType: case m.name.s of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize) - of "sink": setMagicType(c.config, m, tySink, 0) + of "sink": setMagicType(c.config, m, tySink, szUncomputedSize) else: localError(c.config, m.info, errTypeExpected) else: localError(c.config, m.info, errTypeExpected) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index b05fb37ae..ffa913f1d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -233,7 +233,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = #result = PSym(idTableGet(cl.symMap, s)) #if result == nil: - result = copySym(s, false) + result = copySym(s) incl(result.flags, sfFromGeneric) #idTablePut(cl.symMap, s, result) result.owner = s.owner diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4adf0bed3..d66e8d121 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -432,7 +432,7 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isNone proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = - if fGenericOrigin != nil and last.kind == tyGenericInst and + if fGenericOrigin != nil and last.kind == tyGenericInst and last.len-1 == fGenericOrigin.len: for i in countup(1, sonsLen(fGenericOrigin) - 1): let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i])) @@ -1034,8 +1034,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, template doBind: bool = trDontBind notin flags - # var and static arguments match regular modifier-free types - var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent}), c.calleeSym) + # var, sink and static arguments match regular modifier-free types + var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent, tySink}), c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even # start the param matching process. This could be done in `prepareOperand` # for example, but unfortunately `prepareOperand` is not called in certain @@ -1045,7 +1045,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, return typeRel(c, f, lastSon(aOrig)) if a.kind == tyGenericInst and - skipTypes(f, {tyVar, tyLent}).kind notin { + skipTypes(f, {tyVar, tyLent, tySink}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a)) @@ -1835,7 +1835,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, if destIsGeneric: dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) - if fdest in {isEqual, isGeneric}: + if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar): markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ @@ -2231,8 +2231,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: m.state = csNoMatch return + if formal.typ.kind == tyVar: - if not n.isLValue: + let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg + if arg_converter.kind == nkHiddenCallConv: + if arg_converter.typ.kind != tyVar: + m.state = csNoMatch + m.mutabilityProblem = uint8(f-1) + return + elif not n.isLValue: m.state = csNoMatch m.mutabilityProblem = uint8(f-1) return @@ -2243,6 +2250,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: 0 # iterates over the actual given arguments a = 1 + arg: PNode # current prepared argument m.state = csMatch # until proven otherwise m.call = newNodeI(n.kind, n.info) @@ -2297,7 +2305,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.typedescMatched = false n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].typ = n.sons[a].sons[1].typ - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a].sons[1], n.sons[a].sons[1]) if arg == nil: m.state = csNoMatch @@ -2334,7 +2342,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.typedescMatched = false incl(marker, formal.position) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg != nil and m.baseTypeMatch and container != nil: addSon(container, arg) @@ -2368,7 +2376,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.baseTypeMatch = false m.typedescMatched = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg == nil: m.state = csNoMatch diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 4791788fa..a34383d9f 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -52,6 +52,7 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] = ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added ## ``align`` maximum alignment from all sub nodes + assert n != nil if n.typ != nil and n.typ.size == szIllegalRecursion: result.offset = szIllegalRecursion result.align = szIllegalRecursion @@ -177,7 +178,7 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf proc computeSizeAlign(conf: ConfigRef; typ: PType) = ## computes and sets ``size`` and ``align`` members of ``typ`` - + assert typ != nil let hasSize = typ.size != szUncomputedSize let hasAlign = typ.align != szUncomputedSize @@ -220,7 +221,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = int16(conf.target.ptrSize) of tyString: - if tfHasAsgn in typ.flags: + if conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 else: typ.size = conf.target.ptrSize @@ -237,13 +238,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = # recursive tuplers are not allowed and should be detected in the frontend if base.kind == tyTuple: computeSizeAlign(conf, base) - if base.size == szIllegalRecursion: - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion + if base.size < 0: + typ.size = base.size + typ.align = base.align return typ.align = int16(conf.target.ptrSize) - if typ.kind == tySequence and tfHasAsgn in typ.flags: + if typ.kind == tySequence and conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 else: typ.size = conf.target.ptrSize @@ -310,9 +311,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = for i in countup(0, sonsLen(typ) - 1): let child = typ.sons[i] computeSizeAlign(conf, child) - if child.size == szIllegalRecursion: - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion + if child.size < 0: + typ.size = child.size + typ.align = child.align return maxAlign = max(maxAlign, child.align) sizeAccum = align(sizeAccum, child.align) + child.size @@ -379,7 +380,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = typ.lastSon.size typ.align = typ.lastSon.align - of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: + of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align diff --git a/compiler/trees.nim b/compiler/trees.nim index fb523de9d..ca2360e12 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -92,8 +92,7 @@ proc isCaseObj*(n: PNode): bool = proc isDeepConstExpr*(n: PNode): bool = case n.kind - of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkNilLit: + of nkCharLit..nkNilLit: result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) diff --git a/compiler/vm.nim b/compiler/vm.nim index 5b5b807bb..7e7ec8903 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -960,6 +960,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, "node is not a symbol") + of opcSymIsInstantiationOf: + decodeBC(rkInt) + let a = regs[rb].node + let b = regs[rc].node + if a.kind == nkSym and a.sym.kind in skProcKinds and + b.kind == nkSym and b.sym.kind in skProcKinds: + regs[ra].intVal = + if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 + else: 0 + else: + stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB if rb == 1: diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 25ace3cdd..493078f74 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -147,7 +147,8 @@ type opcTypeTrait, opcMarshalLoad, opcMarshalStore, opcToNarrowInt, - opcSymOwner + opcSymOwner, + opcSymIsInstantiationOf TBlock* = object label*: PSym diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c37ec7c6b..1f2a3e6d1 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1148,6 +1148,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf) of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, dest, opcSymIsInstantiationOf) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -1238,8 +1239,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) - of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, - mDotDot: + of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, mDotDot: c.genCall(n, dest) of mExpandToAst: if n.len != 2: @@ -1258,6 +1258,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely") of mRunnableExamples: discard "just ignore any call to runnableExamples" + of mDestroy: discard "ignore calls to the default destructor" else: # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f87ab4508..75873bfe8 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -11,7 +11,7 @@ #import vmdeps, vm from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, - floor, ceil, `mod`, fmod + floor, ceil, `mod` from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir @@ -102,7 +102,6 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(trunc) wrap1f_math(floor) wrap1f_math(ceil) - wrap2f_math(fmod) proc `mod Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1))) diff --git a/doc/advopt.txt b/doc/advopt.txt index 4fabdda9a..7cd72f6c3 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -101,6 +101,7 @@ Advanced options: --listCmd list the commands used to execute external programs --parallelBuild:0|1|... perform a parallel build value = number of processors (0 for auto-detect) + --incremental:on|off only recompile the changed modules (experimental!) --verbosity:0|1|2|3 set Nim's verbosity level (1 is default) --experimental:$1 enable experimental language feature diff --git a/doc/backends.rst b/doc/backends.rst index ef30971c9..a85b39e5e 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -268,7 +268,7 @@ form the Nim code, then link them into a static binary along your main C program:: $ nim c --noMain --noLinking --header:fib.h fib.nim - $ gcc -o m -Inimcache -Ipath/to/nim/lib nimcache/*.c maths.c + $ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c The first command runs the Nim compiler with three special options to avoid generating a ``main()`` function in the generated files, avoid linking the diff --git a/doc/manual.rst b/doc/manual.rst index b39711dfb..a646b7963 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6755,6 +6755,16 @@ routines marked as ``noSideEffect``. func `+` (x, y: int): int +To override the compiler's side effect analysis a ``{.noSideEffect.}`` +pragma block can be used: + +.. code-block:: nim + + func f() = + {.noSideEffect.}: + echo "test" + + compileTime pragma ------------------ The ``compileTime`` pragma is used to mark a proc or variable to be used at diff --git a/doc/tut2.rst b/doc/tut2.rst index 39e3bd89a..d0c6e7247 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -13,7 +13,6 @@ Introduction "Repetition renders the ridiculous reasonable." -- Norman Wildberger - This document is a tutorial for the advanced constructs of the *Nim* programming language. **Note that this document is somewhat obsolete as the** `manual <manual.html>`_ **contains many more examples of the advanced language @@ -652,369 +651,8 @@ avoid a common bug: to forget to close the file. Note how the ``let fn = filename`` statement ensures that ``filename`` is evaluated only once. -Macros -====== - -Macros enable advanced compile-time code transformations, but they cannot -change Nim's syntax. However, this is no real restriction because Nim's -syntax is flexible enough anyway. Macros have to be implemented in pure Nim -code if the `foreign function interface (FFI) -<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but -other than that restriction (which at some point in the future will go away) -you can write any kind of Nim code and the compiler will run it at compile -time. - -There are two ways to write a macro, either *generating* Nim source code and -letting the compiler parse it, or creating manually an abstract syntax tree -(AST) which you feed to the compiler. In order to build the AST one needs to -know how the Nim concrete syntax is converted to an abstract syntax tree -(AST). The AST is documented in the `macros <macros.html>`_ module. - -Once your macro is finished, there are two ways to invoke it: -(1) invoking a macro like a procedure call (expression macros) -(2) invoking a macro with the special ``macrostmt`` - syntax (statement macros) - - -Expression Macros ------------------ - -The following example implements a powerful ``debug`` command that accepts a -variable number of arguments: - -.. code-block:: nim - :test: "nim c $1" - # to work with Nim syntax trees, we need an API that is defined in the - # ``macros`` module: - import macros - - macro debug(n: varargs[untyped]): typed = - # `n` is a Nim AST that contains a list of expressions; - # this macro returns a list of statements (n is passed for proper line - # information): - result = newNimNode(nnkStmtList, n) - # iterate over any argument that is passed to this macro: - for x in n: - # add a call to the statement list that writes the expression; - # `toStrLit` converts an AST to its string representation: - result.add(newCall("write", newIdentNode("stdout"), toStrLit(x))) - # add a call to the statement list that writes ": " - result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) - # add a call to the statement list that writes the expressions value: - result.add(newCall("writeLine", newIdentNode("stdout"), x)) - - var - a: array[0..10, int] - x = "some string" - a[0] = 42 - a[1] = 45 - - debug(a[0], a[1], x) - -The macro call expands to: - -.. code-block:: nim - write(stdout, "a[0]") - write(stdout, ": ") - writeLine(stdout, a[0]) - - write(stdout, "a[1]") - write(stdout, ": ") - writeLine(stdout, a[1]) - - write(stdout, "x") - write(stdout, ": ") - writeLine(stdout, x) - - - -Statement Macros ----------------- - -Statement macros are defined just as expression macros. However, they are -invoked by an expression following a colon. - -The following example outlines a macro that generates a lexical analyzer from -regular expressions: - -.. code-block:: nim - - macro case_token(n: varargs[untyped]): typed = - # creates a lexical analyzer from regular expressions - # ... (implementation is an exercise for the reader :-) - discard - - case_token: # this colon tells the parser it is a macro statement - of r"[A-Za-z_]+[A-Za-z_0-9]*": - return tkIdentifier - of r"0-9+": - return tkInteger - of r"[\+\-\*\?]+": - return tkOperator - else: - return tkUnknown - - -Building your first macro -------------------------- - -To give a footstart to writing macros we will show now how to turn your typical -dynamic code into something that compiles statically. For the exercise we will -use the following snippet of code as the starting point: - -.. code-block:: nim - :test: "nim c $1" - - import strutils, tables - - proc readCfgAtRuntime(cfgFilename: string): Table[string, string] = - let - inputString = readFile(cfgFilename) - var - source = "" - - result = initTable[string, string]() - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - quit("Input needs comma split values, got: " & line) - result[chunks[0]] = chunks[1] - - if result.len < 1: quit("Input file empty!") - - let info = readCfgAtRuntime("data.cfg") - - when isMainModule: - echo info["licenseOwner"] - echo info["licenseKey"] - echo info["version"] - -Presumably this snippet of code could be used in a commercial software, reading -a configuration file to display information about the person who bought the -software. This external file would be generated by an online web shopping cart -to be included along the program containing the license information:: - - version,1.1 - licenseOwner,Hyori Lee - licenseKey,M1Tl3PjBWO2CC48m - -The ``readCfgAtRuntime`` proc will open the given filename and return a -``Table`` from the `tables module <tables.html>`_. The parsing of the file is -done (without much care for handling invalid data or corner cases) using the -`splitLines proc from the strutils module <strutils.html#splitLines>`_. There -are many things which can fail; mind the purpose is explaining how to make -this run at compile time, not how to properly implement a DRM scheme. - -The reimplementation of this code as a compile time proc will allow us to get -rid of the ``data.cfg`` file we would need to distribute along the binary, plus -if the information is really constant, it doesn't make from a logical point of -view to have it *mutable* in a global variable, it would be better if it was a -constant. Finally, and likely the most valuable feature, we can implement some -verification at compile time. You could think of this as a *better unit -testing*, since it is impossible to obtain a binary unless everything is -correct, preventing you to ship to users a broken program which won't start -because a small critical file is missing or its contents changed by mistake to -something invalid. - - -Generating source code -++++++++++++++++++++++ - -Our first attempt will start by modifying the program to generate a compile -time string with the *generated source code*, which we then pass to the -``parseStmt`` proc from the `macros module <macros.html>`_. Here is the -modified source code implementing the macro: - -.. code-block:: nim - :number-lines: - - import macros, strutils - - macro readCfgAndBuildSource(cfgFilename: string): typed = - let - inputString = slurp(cfgFilename.strVal) - var - source = "" - - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - error("Input needs comma split values, got: " & line) - source &= "const cfg" & chunks[0] & "= \"" & chunks[1] & "\"\n" - - if source.len < 1: error("Input file empty!") - result = parseStmt(source) - - readCfgAndBuildSource("data.cfg") - - when isMainModule: - echo cfglicenseOwner - echo cfglicenseKey - echo cfgversion - -The good news is not much has changed! First, we need to change the handling -of the input parameter (line 3). In the dynamic version the -``readCfgAtRuntime`` proc receives a string parameter. However, in the macro -version it is also declared as string, but this is the *outside* interface of -the macro. When the macro is run, it actually gets a ``PNimNode`` object -instead of a string, and we have to call the `strVal proc -<macros.html#strVal>`_ (line 5) from the `macros module <macros.html>`_ to -obtain the string being passed in to the macro. - -Second, we cannot use the `readFile proc <system.html#readFile>`_ from the -`system module <system.html>`_ due to FFI restriction at compile time. If we -try to use this proc, or any other which depends on FFI, the compiler will -error with the message ``cannot evaluate`` and a dump of the macro's source -code, along with a stack trace where the compiler reached before bailing out. -We can get around this limitation by using the `slurp proc -<system.html#slurp>`_ from the `system module <system.html>`_, which was -precisely made for compilation time (just like `gorge <system.html#gorge>`_ -which executes an external program and captures its output). - -The interesting thing is that our macro does not return a runtime `Table -<tables.html#Table>`_ object. Instead, it builds up Nim source code into -the ``source`` variable. For each line of the configuration file a ``const`` -variable will be generated (line 15). To avoid conflicts we prefix these -variables with ``cfg``. In essence, what the compiler is doing is replacing -the line calling the macro with the following snippet of code: - -.. code-block:: nim - const cfgversion = "1.1" - const cfglicenseOwner = "Hyori Lee" - const cfglicenseKey = "M1Tl3PjBWO2CC48m" - -You can verify this yourself adding the line ``echo source`` somewhere at the -end of the macro and compiling the program. Another difference is that instead -of calling the usual `quit proc <system.html#quit>`_ to abort (which we could -still call) this version calls the `error proc <macros.html#error>`_ (line -14). The ``error`` proc has the same behavior as ``quit`` but will dump also -the source and file line information where the error happened, making it -easier for the programmer to find where compilation failed. In this situation -it would point to the line invoking the macro, but **not** the line of -``data.cfg`` we are processing, that's something the macro itself would need -to control. - - -Generating AST by hand -++++++++++++++++++++++ - -To generate an AST we would need to intimately know the structures used by the -Nim compiler exposed in the `macros module <macros.html>`_, which at first -look seems a daunting task. But we can use as helper shortcut the `dumpTree -macro <macros.html#dumpTree>`_, which is used as a statement macro instead of -an expression macro. Since we know that we want to generate a bunch of -``const`` symbols we can create the following source file and compile it to -see what the compiler *expects* from us: - -.. code-block:: nim - :test: "nim c $1" - import macros - - dumpTree: - const cfgversion: string = "1.1" - const cfglicenseOwner = "Hyori Lee" - const cfglicenseKey = "M1Tl3PjBWO2CC48m" - -During compilation of the source code we should see the following lines in the -output (again, since this is a macro, compilation is enough, you don't have to -run any binary):: - - StmtList - ConstSection - ConstDef - Ident !"cfgversion" - Ident !"string" - StrLit 1.1 - ConstSection - ConstDef - Ident !"cfglicenseOwner" - Empty - StrLit Hyori Lee - ConstSection - ConstDef - Ident !"cfglicenseKey" - Empty - StrLit M1Tl3PjBWO2CC48m - -With this output we have a better idea of what kind of input the compiler -expects. We need to generate a list of statements. For each constant the source -code generates a ``ConstSection`` and a ``ConstDef``. If we were to move all -the constants to a single ``const`` block we would see only a single -``ConstSection`` with three children. - -Maybe you didn't notice, but in the ``dumpTree`` example the first constant -explicitly specifies the type of the constant. That's why in the tree output -the two last constants have their second child ``Empty`` but the first has a -string identifier. So basically a ``const`` definition is made up from an -identifier, optionally a type (can be an *empty* node) and the value. Armed -with this knowledge, let's look at the finished version of the AST building -macro: - -.. code-block:: nim - :number-lines: - - import macros, strutils - - macro readCfgAndBuildAST(cfgFilename: string): typed = - let - inputString = slurp(cfgFilename.strVal) - - result = newNimNode(nnkStmtList) - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - error("Input needs comma split values, got: " & line) - var - section = newNimNode(nnkConstSection) - constDef = newNimNode(nnkConstDef) - constDef.add(newIdentNode("cfg" & chunks[0])) - constDef.add(newEmptyNode()) - constDef.add(newStrLitNode(chunks[1])) - section.add(constDef) - result.add(section) - - if result.len < 1: error("Input file empty!") - - readCfgAndBuildAST("data.cfg") - - when isMainModule: - echo cfglicenseOwner - echo cfglicenseKey - echo cfgversion - -Since we are building on the previous example generating source code, we will -only mention the differences to it. Instead of creating a temporary ``string`` -variable and writing into it source code as if it were written *by hand*, we -use the ``result`` variable directly and create a statement list node -(``nnkStmtList``) which will hold our children (line 7). - -For each input line we have to create a constant definition (``nnkConstDef``) -and wrap it inside a constant section (``nnkConstSection``). Once these -variables are created, we fill them hierarchichally (line 17) like the -previous AST dump tree showed: the constant definition is a child of the -section definition, and the constant definition has an identifier node, an -empty node (we let the compiler figure out the type), and a string literal -with the value. - -A last tip when writing a macro: if you are not sure the AST you are building -looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use -it *inside* the macro you are writting/debugging. Instead ``echo`` the string -generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this -example you add ``echo treeRepr(result)`` you should get the same output as -using the ``dumpTree`` macro, but of course you can call that at any point of -the macro where you might be having troubles. - -Example Templates and Macros -============================ - -Lifting Procs -+++++++++++++ +Example: Lifting Procs +---------------------- .. code-block:: nim :test: "nim c $1" @@ -1039,36 +677,6 @@ Lifting Procs liftScalarProc(sqrt) # make sqrt() work for sequences echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0] -Identifier Mangling -+++++++++++++++++++ - -.. code-block:: nim - proc echoHW() = - echo "Hello world" - proc echoHW0() = - echo "Hello world 0" - proc echoHW1() = - echo "Hello world 1" - - template joinSymbols(a, b: untyped): untyped = - `a b`() - - joinSymbols(echo, HW) - - macro str2Call(s1, s2): typed = - result = newNimNode(nnkStmtList) - for i in 0..1: - # combines s1, s2 and an integer into an proc identifier - # that is called in a statement list - result.add(newCall(!($s1 & $s2 & $i))) - - str2Call("echo", "HW") - - # Output: - # Hello world - # Hello world 0 - # Hello world 1 - Compilation to JavaScript ========================= @@ -1083,3 +691,9 @@ JavaScript-compatible code you should remember the following: - ``cstring`` in JavaScript means JavaScript string. It is a good practice to use ``cstring`` only when it is semantically appropriate. E.g. don't use ``cstring`` as a binary data buffer. + + +Part 3 +====== + +Next part will be entirely about metaprogramming via macros: `Part III <tut3.html>`_ diff --git a/doc/tut3.rst b/doc/tut3.rst new file mode 100644 index 000000000..5590db8fe --- /dev/null +++ b/doc/tut3.rst @@ -0,0 +1,354 @@ +======================= +Nim Tutorial (Part III) +======================= + +:Author: Arne Döring +:Version: |nimversion| + +.. contents:: + + +Introduction +============ + + "With Great Power Comes Great Responsibility." -- Spider Man's Uncle + +This document is a tutorial about Nim's macro system. +A macro is a function that is executed at compile time and transforms +a Nim syntax tree into a different tree. + +Examples of things that can be implemented in macros: + + * An assert macro that prints both sides of a comparison operator, if +the assertion fails. ``myAssert(a == b)`` is converted to +``if a != b: quit($a " != " $b)`` + + * A debug macro that prints the value and the name of the symbol. +``myDebugEcho(a)`` is converted to ``echo "a: ", a`` + + * Symbolic differentiation of an expression. +``diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)`` is converted to +``3*a*pow(x,2) + 2*a*x + c`` + + +Macro Arguments +--------------- + +The types of macro arguments have two faces. One face is used for +the overload resolution, and the other face is used within the macro +body. For example, if ``macro foo(arg: int)`` is called in an +expression ``foo(x)``, ``x`` has to be of a type compatible to int, but +*within* the macro's body ``arg`` has the type ``NimNode``, not ``int``! +Why it is done this way will become obvious later, when we have seen +concrete examples. + +There are two ways to pass arguments to a macro, an argument can be +either ``typed`` or ``untyped``. + + +Untyped Arguments +----------------- + +Untyped macro arguments are passed to the macro before they are +semantically checked. This means the syntax tree that is passed down +to the macro does not need to make sense for Nim yet, the only +limitation is that it needs to be parseable. Usually the macro does +not check the argument either but uses it in the transformation's +result somehow. The result of a macro expansion is always checked +by the compiler, so apart from weird error messages nothing bad +can happen. + +The downside for an ``untyped`` argument is that these do not play +well with Nim's overloading resolution. + +The upside for untyped arguments is that the syntax tree is +quite predictable and less complex compared to its ``typed`` +counterpart. + + +Typed Arguments +--------------- + +For typed arguments, the semantic checker runs on the argument and +does transformations on it, before it is passed to the macro. Here +identifier nodes are resolved as symbols, implicit type +conversions are visible in the tree as calls, templates are +expanded and probably most importantly, nodes have type information. +Typed arguments can have the type ``typed`` in the arguments list. +But all other types, such as ``int``, ``float`` or ``MyObjectType`` +are typed arguments as well, and they are passed to the macro as a +syntax tree. + + +Static Arguments +---------------- + +Static arguments are a way to pass values as values and not as syntax +tree nodes to a macro. For example for ``macro foo(arg: static[int])`` +in the expression ``foo(x)``, ``x`` needs to be an integer constant, +but in the macro body ``arg`` is just like a normal parameter of type +``int``. + +.. code-block:: nim + + import macros + + macro myMacro(arg: static[int]): untyped = + echo arg # just an int (7), not ``NimNode`` + + myMacro(1 + 2 * 3) + + +Code blocks as arguments +------------------------ + +It is possible to pass the last argument of a call expression in a +separate code block with indentation. For example the following code +example is a valid (but not a recommended) way to call ``echo``: + +.. code-block:: nim + + echo "Hello ": + let a = "Wor" + let b = "ld!" + a & b + +For macros this way of calling is very useful; syntax trees of arbitrary +complexity can be passed to macros with this notation. + + +The Syntax Tree +--------------- + +In order to build a Nim syntax tree one needs to know how Nim source +code is represented as a syntax tree, and how such a tree needs to +look like so that the Nim compiler will understand it. The nodes of the +Nim syntax tree are documented in the `macros <macros.html>`_ module. +But a more interactive way to explore the Nim +syntax tree is with ``macros.treeRepr``, it converts a syntax tree +into a multi line string for printing on the console. It can be used +to explore how the argument expressions are represented in tree form +and for debug printing of generated syntax tree. ``dumpTree`` is a +predefined macro that just prints its argument in tree representation, +but does nothing else. Here is an example of such a tree representation: + +.. code-block:: nim + + dumpTree: + var mt: MyType = MyType(a:123.456, b:"abcdef") + + # output: + # StmtList + # VarSection + # IdentDefs + # Ident "mt" + # Ident "MyType" + # ObjConstr + # Ident "MyType" + # ExprColonExpr + # Ident "a" + # FloatLit 123.456 + # ExprColonExpr + # Ident "b" + # StrLit "abcdef" + + +Custom sematic checking +----------------------- + +The first thing that a macro should do with its arguments is to check +if the argument is in the correct form. Not every type of wrong input +needs to be caught here, but anything that could cause a crash during +macro evaluation should be caught and create a nice error message. +``macros.expectKind`` and ``macros.expectLen`` are a good start. If +the checks need to be more complex, arbitrary error messages can +be created with the ``macros.error`` proc. + +.. code-block:: nim + + macro myAssert(arg: untyped): untyped = + arg.expectKind nnkInfix + + +Generating Code +--------------- + +There are two ways to generate the code. Either by creating the syntax +tree with expressions that contain a lot of calls to ``newTree`` and +``newLit``, or with ``quote do:`` expressions. The first option offers +the best low level control for the syntax tree generation, but the +second option is much less verbose. If you choose to create the syntax +tree with calls to ``newTree`` and ``newLit`` the macro +``marcos.dumpAstGen`` can help you with the verbosity. ``quote do:`` +allows you to write the code that you want to generate literally, +backticks are used to insert code from ``NimNode`` symbols into the +generated expression. This means that you can't use backticks within +``quote do:`` for anything else than injecting symbols. Make sure to +inject only symbols of type ``NimNode`` into the generated syntax +tree. You can use ``newLit`` to convert arbitrary values into +expressions trees of type ``NimNode`` so that it is safe to inject +them into the tree. + + +.. code-block:: nim + :test: "nim c $1" + + import macros + + type + MyType = object + a: float + b: string + + macro myMacro(arg: untyped): untyped = + var mt: MyType = MyType(a:123.456, b:"abcdef") + + # ... + + let mtLit = newLit(mt) + + result = quote do: + echo `arg` + echo `mtLit` + + myMacro("Hallo") + +The call to ``myMacro`` will generate the following code: + +.. code-block:: nim + echo "Hallo" + echo MyType(a: 123.456'f64, b: "abcdef") + + +Building your first macro +------------------------- + +To give a footstart to writing macros we will show now how to +implement the ``myDebug`` macro mentioned earlier. The first thing to +do is to build a simple example of the macro usage, and then just +print the argument. This way it is possible to get an idea of a +correct argument should be look like. + +.. code-block:: nim + :test: "nim c $1" + + import macros + + macro myAssert(arg: untyped): untyped = + echo arg.treeRepr + + let a = 1 + let b = 2 + + myAssert(a != b) + +.. code-block:: + + Infix + Ident "!=" + Ident "a" + Ident "b" + + +From the output it is possible to see that the information that the +argument is an infix operator (node kind is "Infix"), as well as that the two +operands are at index 1 and 2. With this information the actual +macro can be written. + +.. code-block:: nim + :test: "nim c $1" + + import macros + + macro myAssert(arg: untyped): untyped = + # all node kind identifiers are prefixed with "nnk" + arg.expectKind nnkInfix + arg.expectLen 3 + # operator as string literal + let op = newLit(" " & arg[0].repr & " ") + let lhs = arg[1] + let rhs = arg[2] + + result = quote do: + if not `arg`: + raise newException(AssertionError,$`lhs` & `op` & $`rhs`) + + let a = 1 + let b = 2 + + myAssert(a != b) + myAssert(a == b) + + +This is the code that will be generated. To debug what the macro +actually generated, the statement ``echo result.repr`` can be used, in +the last line of the macro. It is also the statement that has been +used to get this output. + +.. code-block:: nim + if not (a != b): + raise newException(AssertionError, $a & " != " & $b) + +With Power Comes Responsibility +------------------------------- + +Macros are very powerful. A good advice is to use them as little as +possible, but as much as necessary. Macros can change the semantics of +expressions, making the code incomprehensible for anybody who does not +know exactly what the macro does with it. So whenever a macro is not +necessary and the same logic can be implemented using templates or +generics, it is probably better not to use a macro. And when a macro +is used for something, the macro should better have a well written +documentation. For all the people who claim to write only perfectly +self-explanatory code: when it comes to macros, the implementation is +not enough for documentation. + +Limitations +----------- + +Since macros are evaluated in the compiler in the NimVM, macros share +all the limitations of the NimVM. They have to be implemented in pure Nim +code. Macros can start external processes on the shell, but they +cannot call C functions except from those that are built in the +compiler. + + +More Examples +============= + +This tutorial can only cover the basics of the macro system. There are +macros out there that could be an inspiration for you of what is +possible with it. + + +Strformat +--------- + +In the Nim standard library, the ``strformat`` library provides a +macro that parses a string literal at compile time. Parsing a string +in a macro like here is generally not recommended. The parsed AST +cannot have type information, and parsing implemented on the VM is +generally not very fast. Working on AST nodes is almost always the +recommended way. But still ``strformat`` is a good example for a +practical use case for a macro that is slightly more complex that the +``assert`` macro. + +`Strformat <https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280>`_ + +Ast Pattern Matching +-------------------- + +Ast Pattern Matching is a macro library to aid in writing complex +macros. This can be seen as a good example of how to repurpose the +Nim syntax tree with new semantics. + +`Ast Pattern Matching <https://github.com/krux02/ast-pattern-matching>`_ + +OpenGL Sandbox +-------------- + +This project has a working Nim to GLSL compiler written entirely in +macros. It scans recursively though all used function symbols to +compile them so that cross library functions can be executed on the GPU. + +`OpenGL Sandbox <https://github.com/krux02/opengl-sandbox>`_ diff --git a/examples/tunit.nim b/examples/tunit.nim index 785b9aa5e..bc447812d 100644 --- a/examples/tunit.nim +++ b/examples/tunit.nim @@ -1,3 +1,4 @@ + import unittest, macros @@ -44,4 +45,3 @@ test "arithmetic failure": expect(ArithmeticError, CatchableError): discard foo() - diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim index f652f0d85..5189bb762 100644 --- a/lib/core/allocators.nim +++ b/lib/core/allocators.nim @@ -11,19 +11,36 @@ type AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator ThreadLocal ## the allocator is thread local only. ZerosMem ## the allocator always zeros the memory on an allocation - Allocator* = ptr object {.inheritable.} + Allocator* = ptr AllocatorObj + AllocatorObj* {.inheritable.} = object alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.} realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} deallocAll*: proc (a: Allocator) {.nimcall.} flags*: set[AllocatorFlag] + allocCount: int + deallocCount: int var localAllocator {.threadvar.}: Allocator sharedAllocator: Allocator + allocatorStorage {.threadvar.}: AllocatorObj proc getLocalAllocator*(): Allocator = result = localAllocator + if result == nil: + result = addr allocatorStorage + result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} = + result = system.alloc(size) + inc a.allocCount + result.dealloc = proc (a: Allocator; p: pointer; size: int) {.nimcall.} = + system.dealloc(p) + inc a.deallocCount + result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} = + result = system.realloc(p, newSize) + result.deallocAll = nil + result.flags = {ThreadLocal} + localAllocator = result proc setLocalAllocator*(a: Allocator) = localAllocator = a @@ -34,15 +51,6 @@ proc getSharedAllocator*(): Allocator = proc setSharedAllocator*(a: Allocator) = sharedAllocator = a -when false: - proc alloc*(size: int; alignment: int = 8): pointer = - let a = getCurrentAllocator() - result = a.alloc(a, size, alignment) - - proc dealloc*(p: pointer; size: int) = - let a = getCurrentAllocator() - a.dealloc(a, p, size) - - proc realloc*(p: pointer; oldSize, newSize: int): pointer = - let a = getCurrentAllocator() - result = a.realloc(a, p, oldSize, newSize) +proc allocCounters*(): (int, int) = + let a = getLocalAllocator() + result = (a.allocCount, a.deallocCount) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d74d86bf6..f45ca3f82 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -274,6 +274,12 @@ when defined(nimHasSymOwnerInMacro): ## result is also mnde of kind nnkSym if owner exists otherwise ## nnkNilLit is returned +when defined(nimHasInstantiationOfInMacro): + proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.} + ## check if proc symbol is instance of the generic proc symbol + ## useful to check proc symbols against generic symbols + ## returned by `bindSym` + proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## with 'getType' you can access the node's `type`:idx:. A Nim type is ## mapped to a Nim AST too, so it's slightly confusing but it means the same diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index fb81a30de..a41ef10ab 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -85,7 +85,7 @@ type proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = # we have to use type erasure here as Nim does not support generic # compilerProcs. Oh well, this will all be inlined anyway. - if cap <= 0: + if cap > 0: let region = getLocalAllocator() var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator))) p.region = region @@ -94,23 +94,25 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = else: result = nil -proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} = - if len+addlen <= len: - result = p - elif p == nil: - result = newSeqPayload(len+addlen, elemSize) - else: - # Note: this means we cannot support things that have internal pointers as - # they get reallocated here. This needs to be documented clearly. - var p = cast[ptr PayloadBase](p) - let region = if p.region == nil: getLocalAllocator() else: p.region - let cap = max(resize(p.cap), len+addlen) - var q = cast[ptr PayloadBase](region.realloc(region, p, - sizeof(int) + sizeof(Allocator) + elemSize * p.cap, - sizeof(int) + sizeof(Allocator) + elemSize * cap)) - q.region = region - q.cap = cap - result = q +proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {. + compilerRtl, noSideEffect.} = + {.noSideEffect.}: + if len+addlen <= len: + result = p + elif p == nil: + result = newSeqPayload(len+addlen, elemSize) + else: + # Note: this means we cannot support things that have internal pointers as + # they get reallocated here. This needs to be documented clearly. + var p = cast[ptr PayloadBase](p) + let region = if p.region == nil: getLocalAllocator() else: p.region + let cap = max(resize(p.cap), len+addlen) + var q = cast[ptr PayloadBase](region.realloc(region, p, + sizeof(int) + sizeof(Allocator) + elemSize * p.cap, + sizeof(int) + sizeof(Allocator) + elemSize * cap)) + q.region = region + q.cap = cap + result = q proc shrink*[T](x: var seq[T]; newLen: Natural) = sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" @@ -124,18 +126,19 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = let oldLen = x.len if newLen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr x) - - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) + if xu.p == nil or xu.p.cap < newLen: + xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) xu.len = newLen for i in oldLen .. newLen-1: - x.data[i] = value + xu.p.data[i] = value proc setLen[T](s: var seq[T], newlen: Natural) = - if newlen < s.len: - shrink(s, newLen) - else: - var v: T # get the default value of 'v' - grow(s, newLen, v) + {.noSideEffect.}: + if newlen < s.len: + shrink(s, newLen) + else: + var v: T # get the default value of 'v' + grow(s, newLen, v) when false: proc resize[T](s: var NimSeqV2[T]) = diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 14613c50d..cfb8f8f5d 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -106,6 +106,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) of 4: d = int(cast[ptr int32](a +% n.offset)[]) + of 8: d = int(cast[ptr int64](a +% n.offset)[]) else: assert(false) return d diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 307fe2382..e1c59803d 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -69,10 +69,25 @@ template mangleJsName(name: cstring): cstring = inc nameCounter "mangledName" & $nameCounter +# only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring + +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} + +proc toJsKey*[T: enum](text: cstring, t: type T): T = + T(text.toJsKey(int)) + +proc toJsKey*(text: cstring, t: type cstring): cstring = + text + +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} + type + JsKey* = concept a, type T + cstring.toJsKey(T) is type(a) + JsObject* = ref object of JsRoot ## Dynamically typed wrapper around a JavaScript object. - JsAssoc*[K, V] = ref object of JsRoot + JsAssoc*[K: JsKey, V] = ref object of JsRoot ## Statically typed wrapper around a JavaScript object. js* = JsObject @@ -104,7 +119,7 @@ type proc newJsObject*: JsObject {. importcpp: "{@}" .} ## Creates a new empty JsObject -proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {. importcpp: "{@}" .} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks @@ -176,21 +191,19 @@ proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .} proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]`*[K: not string, V](obj: JsAssoc[K, V], field: K): V - {. importcpp: getImpl .} - ## Return the value of a property of name `field` from a JsAssoc `obj`. - -proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V +proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V {. importcpp: getImpl .} ## Return the value of a property of name `field` from a JsAssoc `obj`. -proc `[]=`*[K: not string, V](obj: JsAssoc[K, V], field: K, val: V) +proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. -proc `[]=`*[V](obj: JsAssoc[string, V], field: cstring, val: V) - {. importcpp: setImpl .} - ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. +proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = + obj[cstring(field)] + +proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = + obj[cstring(field)] = val proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .} ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison @@ -277,7 +290,7 @@ macro `.()`*(obj: JsObject, result[0][3].add newIdentDefs(paramName, newIdentNode(!"JsObject")) result[1].add args[idx].copyNimTree -macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped): V = ## Experimental dot accessor (get) for type JsAssoc. ## Returns the value of a property of name `field` from a JsObject `x`. @@ -293,7 +306,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`) -macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, value: V): untyped = ## Experimental dot accessor (set) for type JsAssoc. @@ -310,7 +323,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`, `value`) -macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], +macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, args: varargs[untyped]): auto = ## Experimental "method call" operator for type JsAssoc. @@ -354,24 +367,18 @@ iterator keys*(obj: JsObject): cstring = yield k {.emit: "}".} -iterator pairs*[K, V](assoc: JsAssoc[K, V]): (K,V) = +iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = ## Yields tuples of type ``(K, V)``, with the first entry ## being a `key` in the JsAssoc and the second being its corresponding value. - when K is string: - var k: cstring - else: - var k: K + var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} {.emit: " `v`=`assoc`[`k`];".} - when K is string: - yield ($k, v) - else: - yield (k, v) + yield (k.toJsKey(K), v) {.emit: "}".} -iterator items*[K,V](assoc: JSAssoc[K,V]): V = +iterator items*[K, V](assoc: JSAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} @@ -380,18 +387,12 @@ iterator items*[K,V](assoc: JSAssoc[K,V]): V = yield v {.emit: "}".} -iterator keys*[K,V](assoc: JSAssoc[K,V]): K = +iterator keys*[K: JsKey, V](assoc: JSAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. - when K is string: - var k: cstring - else: - var k: K + var k: cstring {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - when K is string: - yield $k - else: - yield k + yield k.toJsKey(K) {.emit: "}".} # Literal generation diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 4e4cf7e0e..335207f54 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -33,4 +33,3 @@ when not defined(createNimRtl): import parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes, os, osproc, times - diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 0c66aa2b9..175f6a61d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -823,6 +823,12 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {. proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "<sys/socket.h>".} +proc CMSG_SPACE*(len: csize): csize {. + importc, header: "<sys/socket.h>".} + +proc CMSG_LEN*(len: csize): csize {. + importc, header: "<sys/socket.h>".} + const INVALID_SOCKET* = SocketHandle(-1) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 59ef06459..e33ddeaf0 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -218,7 +218,7 @@ when defineSsl: var data = await recv(socket.fd.AsyncFD, BufferSize, flags) let length = len(data) if length > 0: - let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint) + let ret = bioWrite(socket.bioIn, addr data[0], length.cint) if ret < 0: raiseSSLError() elif length == 0: diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e8ea675f5..be10780ff 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -504,36 +504,76 @@ template anyIt*(s, pred: untyped): bool = break result -template toSeq*(iter: untyped): untyped = - ## Transforms any iterator into a sequence. - ## - ## Example: - ## - ## .. code-block:: - ## let - ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - ## if x mod 2 == 1: - ## result = true) - ## assert odd_numbers == @[1, 3, 5, 7, 9] - - # Note: see also `mapIt` for explanation of some of the implementation - # subtleties. - when compiles(iter.len): +template toSeq1(s: not iterator): untyped = + # overload for typed but not iterator + type outType = type(items(s)) + when compiles(s.len): block: - evalOnceAs(iter2, iter, true) - var result = newSeq[type(iter)](iter2.len) + evalOnceAs(s2, s, compiles((let _ = s))) var i = 0 - for x in iter2: - result[i] = x - inc i + var result = newSeq[outType](s2.len) + for it in s2: + result[i] = it + i += 1 result else: - var result: seq[type(iter)] = @[] - for x in iter: - result.add(x) + var result: seq[outType] = @[] + for it in s: + result.add(it) + result + +template toSeq2(iter: iterator): untyped = + # overload for iterator + evalOnceAs(iter2, iter(), false) + when compiles(iter2.len): + var i = 0 + var result = newSeq[type(iter2)](iter2.len) + for x in iter2: + result[i] = x + inc i + result + else: + type outType = type(iter2()) + var result: seq[outType] = @[] + when compiles(iter2()): + evalOnceAs(iter4, iter, false) + let iter3=iter4() + for x in iter3(): + result.add(x) + else: + for x in iter2(): + result.add(x) result +template toSeq*(iter: untyped): untyped = + ## Transforms any iterable into a sequence. + runnableExamples: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1)) + doAssert odd_numbers == @[1, 3, 5, 7, 9] + + when compiles(toSeq1(iter)): + toSeq1(iter) + elif compiles(toSeq2(iter)): + toSeq2(iter) + else: + # overload for untyped, eg: `toSeq(myInlineIterator(3))` + when compiles(iter.len): + block: + evalOnceAs(iter2, iter, true) + var result = newSeq[type(iter)](iter2.len) + var i = 0 + for x in iter2: + result[i] = x + inc i + result + else: + var result: seq[type(iter)] = @[] + for x in iter: + result.add(x) + result + template foldl*(sequence, operation: untyped): untyped = ## Template to fold a sequence from left to right, returning the accumulation. ## @@ -1033,12 +1073,72 @@ when isMainModule: assert anyIt(anumbers, it > 9) == false block: # toSeq test - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert odd_numbers == @[1, 3, 5, 7, 9] + block: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9] + + block: + doAssert [1,2].toSeq == @[1,2] + doAssert @[1,2].toSeq == @[1,2] + + doAssert @[1,2].toSeq == @[1,2] + doAssert toSeq(@[1,2]) == @[1,2] + + block: + iterator myIter(seed:int):auto= + for i in 0..<seed: + yield i + doAssert toSeq(myIter(2)) == @[0, 1] + + block: + iterator myIter():auto{.inline.}= + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + iterator myIter():int {.closure.} = + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + proc myIter():auto= + iterator ret():int{.closure.}= + yield 1 + yield 2 + result = ret + + doAssert myIter().toSeq == @[1,2] + doAssert toSeq(myIter()) == @[1,2] + + block: + proc myIter(n:int):auto= + var counter = 0 + iterator ret():int{.closure.}= + while counter<n: + yield counter + counter.inc + result = ret + + block: + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0,1,2] + block: + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0,1,2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0,1,2] + doAssert toSeq(myIter(3)) == @[0,1,2] block: # tests https://github.com/nim-lang/Nim/issues/7187 diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 841985605..b7498b1c5 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1208,7 +1208,9 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, for i in 1..client.maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) - result = await client.requestAux(redirectTo, httpMethod, body, headers) + # Guarantee method for HTTP 307: see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 + var meth = if result.status == "307": httpMethod else: "GET" + result = await client.requestAux(redirectTo, meth, body, headers) lastURL = redirectTo @@ -1227,36 +1229,49 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## be closed. result = await request(client, url, $httpMethod, body, headers) +proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync.} = + ## Returns the content of a response as a string. + ## + ## A ``HttpRequestError`` will be raised if the server responds with a + ## client error (status code 4xx) or a server error (status code 5xx). + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) + else: + return await resp.bodyStream.readAll() + +proc head*(client: HttpClient | AsyncHttpClient, + url: string): Future[Response | AsyncResponse] {.multisync.} = + ## Connects to the hostname specified by the URL and performs a HEAD request. + ## + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, HttpHEAD) + proc get*(client: HttpClient | AsyncHttpClient, url: string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a GET request. ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. + ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpGET) proc getContent*(client: HttpClient | AsyncHttpClient, url: string): Future[string] {.multisync.} = - ## Connects to the hostname specified by the URL and performs a GET request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. - ## - ## A ``HttpRequestError`` will be raised if the server responds with a - ## client error (status code 4xx) or a server error (status code 5xx). + ## Connects to the hostname specified by the URL and returns the content of a GET request. let resp = await get(client, url) - if resp.code.is4xx or resp.code.is5xx: - raise newException(HttpRequestError, resp.status) - else: - return await resp.bodyStream.readAll() + return await responseContent(resp) -proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", - multipart: MultipartData = nil): Future[Response | AsyncResponse] - {.multisync.} = - ## Connects to the hostname specified by the URL and performs a POST request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. +proc delete*(client: HttpClient | AsyncHttpClient, + url: string): Future[Response | AsyncResponse] {.multisync.} = + ## Connects to the hostname specified by the URL and performs a DELETE request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, HttpDELETE) + +proc deleteContent*(client: HttpClient | AsyncHttpClient, + url: string): Future[string] {.multisync.} = + ## Connects to the hostname specified by the URL and returns the content of a DELETE request. + let resp = await delete(client, url) + return await responseContent(resp) + +proc makeRequestContent(body = "", multipart: MultipartData = nil): (string, HttpHeaders) = let (mpContentType, mpBody) = format(multipart) # TODO: Support FutureStream for `body` parameter. template withNewLine(x): untyped = @@ -1265,38 +1280,59 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", else: x var xb = mpBody.withNewLine() & body - var headers = newHttpHeaders() if multipart != nil: headers["Content-Type"] = mpContentType headers["Content-Length"] = $len(xb) + return (xb, headers) - result = await client.requestAux(url, $HttpPOST, xb, headers) - # Handle redirects. - var lastURL = url - for i in 1..client.maxRedirects: - if result.status.redirection(): - let redirectTo = getNewLocation(lastURL, result.headers) - var meth = if result.status != "307": HttpGet else: HttpPost - result = await client.requestAux(redirectTo, $meth, xb, headers) - lastURL = redirectTo +proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a POST request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPOST, xb, headers) proc postContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = - ## Connects to the hostname specified by the URL and performs a POST request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. - ## - ## A ``HttpRequestError`` will be raised if the server responds with a - ## client error (status code 4xx) or a server error (status code 5xx). + ## Connects to the hostname specified by the URL and returns the content of a POST request. let resp = await post(client, url, body, multipart) - if resp.code.is4xx or resp.code.is5xx: - raise newException(HttpRequestError, resp.status) - else: - return await resp.bodyStream.readAll() + return await responseContent(resp) + +proc put*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a PUT request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPUT, xb, headers) + +proc putContent*(client: HttpClient | AsyncHttpClient, url: string, + body = "", + multipart: MultipartData = nil): Future[string] + {.multisync.} = + ## Connects to the hostname specified by the URL andreturns the content of a PUT request. + let resp = await put(client, url, body, multipart) + return await responseContent(resp) + +proc patch*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a PATCH request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPATCH, xb, headers) + +proc patchContent*(client: HttpClient | AsyncHttpClient, url: string, + body = "", + multipart: MultipartData = nil): Future[string] + {.multisync.} = + ## Connects to the hostname specified by the URL and returns the content of a PATCH request. + let resp = await patch(client, url, body, multipart) + return await responseContent(resp) proc downloadFile*(client: HttpClient, url: string, filename: string) = ## Downloads ``url`` and saves it to ``filename``. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0e43e18ca..31610a59e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2424,6 +2424,15 @@ proc isHidden*(path: string): bool {.noNimScript.} = let fileName = lastPathPart(path) result = len(fileName) >= 2 and fileName[0] == '.' and fileName != ".." +proc getCurrentProcessId*(): int {.noNimScript.} = + ## return current process ID. See also ``osproc.processID(p: Process)``. + when defined(windows): + proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcessId".} + result = GetCurrentProcessId().int + else: + result = getpid() + {.pop.} proc setLastModificationTime*(file: string, t: times.Time) {.noNimScript.} = diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 0cf2171de..b2239b9c5 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -64,6 +64,7 @@ const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. proc execProcess*(command: string, + workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, @@ -154,7 +155,7 @@ proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true iff the process `p` is still running. Returns immediately. proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = - ## returns `p`'s process ID. + ## returns `p`'s process ID. See also ``os.getCurrentProcessId()``. return p.id proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, @@ -349,12 +350,13 @@ proc select*(readfds: var seq[Process], timeout = 500): int when not defined(useNimRtl): proc execProcess(command: string, + workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): TaintedString = - var p = startProcess(command, args=args, env=env, options=options) + var p = startProcess(command, workingDir=workingDir, args=args, env=env, options=options) var outp = outputStream(p) result = TaintedString"" var line = newStringOfCap(120).TaintedString @@ -1323,6 +1325,12 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim") var p = startProcess(command, options=options + {poEvalCommand}) var outp = outputStream(p) + + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + close inputStream(p) + result = (TaintedString"", -1) var line = newStringOfCap(120).TaintedString while true: @@ -1333,3 +1341,4 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { result[1] = peekExitCode(p) if result[1] != -1: break close(p) + diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index f68baaf6b..fb4bc19af 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -129,8 +129,8 @@ proc parseIdent*(s: string, ident: var string, start = 0): int = result = i-start proc parseIdent*(s: string, start = 0): string = - ## parses an identifier and stores it in ``ident``. - ## Returns the parsed identifier or an empty string in case of an error. + ## parses an identifier and returns it or an empty string in + ## case of an error. result = "" var i = start if i < s.len and s[i] in IdentStartChars: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index a2c2c1f88..c458d51eb 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -231,4 +231,8 @@ when isMainModule: except RangeError: discard + + # don't use causes integer overflow + doAssert compiles(random[int](low(int) .. high(int))) + main() diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b8f76276a..010551b5a 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -22,7 +22,7 @@ let time = cpuTime() sleep(100) # replace this with something to be timed - echo "Time taken: ",cpuTime() - time + echo "Time taken: ", cpuTime() - time echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm") echo "Using predefined formats: ", getClockStr(), " ", getDateStr() @@ -158,7 +158,9 @@ when defined(posix): type CTime = posix.Time - var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid + var + realTimeClockId {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid + cpuClockId {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid proc gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "<sys/time.h>".} @@ -745,8 +747,7 @@ proc abs*(a: Duration): Duration = initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond) proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = - ## Converts a broken-down time structure to - ## calendar time representation. + ## Converts a ``DateTime`` to a ``Time`` representing the same point in time. let epochDay = toEpochday(dt.monthday, dt.month, dt.year) var seconds = epochDay * secondsInDay seconds.inc dt.hour * secondsInHour @@ -840,6 +841,11 @@ proc `$`*(zone: Timezone): string = proc `==`*(zone1, zone2: Timezone): bool = ## Two ``Timezone``'s are considered equal if their name is equal. + if system.`==`(zone1, zone2): + return true + if zone1.isNil or zone2.isNil: + return false + runnableExamples: doAssert local() == local() doAssert local() != utc() @@ -1047,7 +1053,7 @@ proc local*(t: Time): DateTime = t.inZone(local()) proc getTime*(): Time {.tags: [TimeEffect], benign.} = - ## Gets the current time as a ``Time`` with nanosecond resolution. + ## Gets the current time as a ``Time`` with up to nanosecond resolution. when defined(JS): let millis = newDate().getTime() let seconds = convert(Milliseconds, Seconds, millis) @@ -1061,7 +1067,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int)) elif defined(posix): var ts: Timespec - discard clock_gettime(CLOCK_REALTIME, ts) + discard clock_gettime(realTimeClockId, ts) result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) elif defined(windows): var f: FILETIME @@ -1141,16 +1147,16 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval = result = ti1 + (-ti2) proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current date as a string of the format ``YYYY-MM-DD``. - var ti = now() - result = $ti.year & '-' & intToStr(ord(ti.month), 2) & - '-' & intToStr(ti.monthday, 2) + ## Gets the current local date as a string of the format ``YYYY-MM-DD``. + var dt = now() + result = $dt.year & '-' & intToStr(ord(dt.month), 2) & + '-' & intToStr(dt.monthday, 2) proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current clock time as a string of the format ``HH:MM:SS``. - var ti = now() - result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & - ':' & intToStr(ti.second, 2) + ## Gets the current local clock time as a string of the format ``HH:MM:SS``. + var dt = now() + result = intToStr(dt.hour, 2) & ':' & intToStr(dt.minute, 2) & + ':' & intToStr(dt.second, 2) proc toParts* (ti: TimeInterval): TimeIntervalParts = ## Converts a `TimeInterval` into an array consisting of its time units, @@ -1382,7 +1388,6 @@ proc `==`*(a, b: DateTime): bool = ## Returns true if ``a == b``, that is if both dates represent the same point in time. return a.toTime == b.toTime - proc isStaticInterval(interval: TimeInterval): bool = interval.years == 0 and interval.months == 0 and interval.days == 0 and interval.weeks == 0 @@ -1397,28 +1402,20 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = hours = interval.hours) proc between*(startDt, endDt: DateTime): TimeInterval = - ## Evaluate difference between two dates in ``TimeInterval`` format, so, it - ## will be relative. + ## Gives the difference between ``startDt`` and ``endDt`` as a + ## ``TimeInterval``. ## - ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in - ## different ``TimeZone's``. - ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC. + ## **Warning:** This proc currently gives very few guarantees about the + ## result. ``a + between(a, b) == b`` is **not** true in general + ## (it's always true when UTC is used however). Neither is it guaranteed that + ## all components in the result will have the same sign. The behavior of this + ## proc might change in the future. runnableExamples: - var a = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 0, minute = 59, second = 59, nanosecond = 1, - zone = utc()).local - var b = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 1, minute = 1, second = 1, nanosecond = 0, - zone = utc()).local - doAssert between(a, b) == initTimeInterval( - nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1) - - a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc()) - b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz") - doAssert between(a, b) == initTimeInterval(hours=1, days=2) - ## Though, here correct answer should be 1 day 25 hours (cause this day in - ## this tz is actually 26 hours). That's why operating different TZ is - ## discouraged + var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc()) + var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc()) + var ti = initTimeInterval(years = 2, days = 7, hours = 3, seconds = 15) + doAssert between(a, b) == ti + doAssert between(a, b) == -between(b, a) var startDt = startDt.utc() var endDt = endDt.utc() @@ -1546,7 +1543,6 @@ proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) = var dur = initDuration(seconds = 1) dur *= 5 doAssert dur == initDuration(seconds = 5) - a = a * b # @@ -1810,7 +1806,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of UUUU: result.add $dt.year of z, zz, zzz, zzzz: - if dt.timezone.name == "Etc/UTC": + if dt.timezone != nil and dt.timezone.name == "Etc/UTC": result.add 'Z' else: result.add if -dt.utcOffset >= 0: '+' else: '-' @@ -2343,7 +2339,15 @@ when not defined(JS): fib.add(fib[^1] + fib[^2]) echo "CPU time [s] ", cpuTime() - t0 echo "Fib is [s] ", fib - result = toFloat(int(getClock())) / toFloat(clocksPerSec) + when defined(posix): + # 'clocksPerSec' is a compile-time constant, possibly a + # rather awful one, so use clock_gettime instead + var ts: Timespec + discard clock_gettime(cpuClockId, ts) + result = toFloat(ts.tv_sec.int) + + toFloat(ts.tv_nsec.int) / 1_000_000_000 + else: + result = toFloat(int(getClock())) / toFloat(clocksPerSec) proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets time after the UNIX epoch (1970) in seconds. It is a float diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 664765954..712cc46c8 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1941,9 +1941,9 @@ proc strip*(s: string, leading = true, trailing = true, e_i = l_i - 1 break dec(i) - let newLen = e_i - s_i + let newLen = e_i - s_i + 1 result = newStringOfCap(newLen) - if e_i > s_i: + if newLen > 0: result.add s[s_i .. e_i] proc repeat*(c: Rune, count: Natural): string {.noSideEffect, @@ -2161,6 +2161,9 @@ when isMainModule: doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "] block stripTests: + doAssert(strip("") == "") + doAssert(strip(" ") == "") + doAssert(strip("y") == "y") doAssert(strip(" foofoofoo ") == "foofoofoo") doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo") diff --git a/lib/system.nim b/lib/system.nim index ace3f5e38..ea767e27a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -373,7 +373,7 @@ when defined(nimArrIdx): x: S) {.noSideEffect, magic: "ArrPut".} when defined(nimNewRuntime): - proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} = + proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## generic `destructor`:idx: implementation that can be overriden. discard proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} = @@ -2522,12 +2522,13 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) - else: - proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = - asm """return `x`""" - if seqToPtr(x) == seqToPtr(y): - return true + if seqToPtr(x) == seqToPtr(y): + return true + else: + var sameObject = false + asm """`sameObject` = `x` === `y`""" + if sameObject: return true when not defined(nimNoNil): if x.isNil or y.isNil: @@ -3460,6 +3461,7 @@ when not defined(JS): #and not defined(nimscript): of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) of 4: d = int(cast[ptr int32](a +% n.offset)[]) + of 8: d = int(cast[ptr int64](a +% n.offset)[]) else: sysAssert(false, "getDiscriminant: invalid n.typ.size") return d @@ -4017,6 +4019,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. + if s.len == 0: return when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -4027,6 +4030,8 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## purposes. when not defined(JS) and not defined(nimscript) and not defined(gcDestructors): var s = cast[PGenericSeq](s) + if s == nil: + s = cast[PGenericSeq](newString(0)) # string literals cannot become 'shallow': if (s.reserved and strlitFlag) == 0: s.reserved = s.reserved or seqShallowFlag @@ -4206,6 +4211,10 @@ when hasAlloc and not defined(nimscript) and not defined(JS) and ## for the implementation of ``spawn``. discard + proc deepCopy*[T](y: T): T = + ## Convenience wrapper around `deepCopy` overload. + deepCopy(result, y) + include "system/deepcopy" proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} = diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index d6c361b2c..70bdc429b 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -17,13 +17,6 @@ const NilLibHandle: LibHandle = nil -proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. - importc: "fwrite", header: "<stdio.h>".} - -proc rawWrite(f: File, s: string|cstring) = - # we cannot throw an exception here! - discard c_fwrite(cstring(s), 1, s.len, f) - proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: stderr.rawWrite("could not load: ") diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 778137668..a6da8f5a3 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -17,8 +17,15 @@ var ## instead of stdmsg.write when printing stacktrace. ## Unstable API. +proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. + importc: "fwrite", header: "<stdio.h>".} + +proc rawWrite(f: File, s: string|cstring) = + # we cannot throw an exception here! + discard c_fwrite(cstring(s), 1, s.len, f) + when not defined(windows) or not defined(guiapp): - proc writeToStdErr(msg: cstring) = write(stdmsg, msg) + proc writeToStdErr(msg: cstring) = rawWrite(stdmsg, msg) else: proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {. diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 74ac68eea..fb20edbbb 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -100,14 +100,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -177,15 +169,6 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes -when hasThreadSupport and hasSharedHeap: - template `--`(x: untyped): untyped = atomicDec(x, rcIncrement) <% rcIncrement - template `++`(x: untyped) = discard atomicInc(x, rcIncrement) -else: - template `--`(x: untyped): untyped = - dec(x, rcIncrement) - x <% rcIncrement - template `++`(x: untyped) = inc(x, rcIncrement) - proc incRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") c.refcount = c.refcount +% rcIncrement @@ -194,28 +177,19 @@ proc incRef(c: PCell) {.inline.} = proc nimGCref(p: pointer) {.compilerProc.} = # we keep it from being collected by pretending it's not even allocated: - add(gch.additionalRoots, usrToCell(p)) - incRef(usrToCell(p)) - -proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = - # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) + let c = usrToCell(p) + add(gch.additionalRoots, c) + incRef(c) proc rtlAddZCT(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) addZCT(gch.zct, c) - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) proc decRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr") gcAssert(c.refcount >=% rcIncrement, "decRef") - if --c.refcount: + c.refcount = c.refcount -% rcIncrement + if c.refcount <% rcIncrement: rtlAddZCT(c) proc nimGCunref(p: pointer) {.compilerProc.} = @@ -239,19 +213,9 @@ template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) = if gch.decStack.d[i] == c: sysAssert(false, msg) -proc GC_addCycleRoot*[T](p: ref T) {.inline.} = - ## adds 'p' to the cycle candidate set for the cycle collector. It is - ## necessary if you used the 'acyclic' pragma for optimization - ## purposes and need to break cycles manually. - rtlAddCycleRoot(usrToCell(cast[pointer](p))) - proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle") - var c = usrToCell(p) - gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr") - if --c.refcount: - rtlAddZCT(c) - sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2") + decRef(usrToCell(p)) sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5") proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = @@ -265,17 +229,8 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = if dest[] != nil: decRef(usrToCell(dest[])) dest[] = src -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc if it is known at compile time that no - # cycle is possible. - if src != nil: - var c = usrToCell(src) - ++c.refcount - if dest[] != nil: - var c = usrToCell(dest[]) - if --c.refcount: - rtlAddZCT(c) - dest[] = src +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = # unsureAsgnRef updates the reference counters only if dest is not on the @@ -440,7 +395,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 incTypeSize typ, size sysAssert(allocInv(gch.region), "rawNewObj begin") - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) @@ -457,7 +411,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = when logGC: writeCell("new cell", res) track("rawNewObj", res, size) gcTrace(res, csAllocated) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -488,7 +441,6 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = # generates a new object and sets its reference counter to 1 incTypeSize typ, size sysAssert(allocInv(gch.region), "newObjRC1 begin") - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") @@ -504,7 +456,6 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = when logGC: writeCell("new cell", res) track("newObjRC1", res, size) gcTrace(res, csAllocated) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -521,7 +472,6 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -577,7 +527,6 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = else: sysAssert(ol.typ != nil, "growObj: 5") zeroMem(ol, sizeof(Cell)) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -665,11 +614,9 @@ proc doOperation(p: pointer, op: WalkOp) = # c_fprintf(stdout, "[GC] decref bug: %p", c) gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef") gcAssert(c.refcount >=% rcIncrement, "doOperation 2") - #c.refcount = c.refcount -% rcIncrement when logGC: writeCell("decref (from doOperation)", c) track("waZctDecref", p, 0) decRef(c) - #if c.refcount <% rcIncrement: addZCT(gch.zct, c) of waPush: add(gch.tempStack, c) of waMarkGlobal: @@ -707,13 +654,13 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: # mark the cell: - objStart.refcount = objStart.refcount +% rcIncrement + incRef(objStart) add(gch.decStack, objStart) when false: if isAllocatedPtr(gch.region, cell): sysAssert false, "allocated pointer but not interior?" # mark the cell: - cell.refcount = cell.refcount +% rcIncrement + incRef(cell) add(gch.decStack, cell) sysAssert(allocInv(gch.region), "gcMark end") @@ -787,11 +734,6 @@ proc unmarkStackAndRegisters(gch: var GcHeap) = for i in 0..gch.decStack.len-1: sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" decRef(d[i]) - #var c = d[i] - # XXX no need for an atomic dec here: - #if --c.refcount: - # addZCT(gch.zct, c) - #sysAssert c.typ != nil, "unmarkStackAndRegisters 2" gch.decStack.len = 0 proc collectCTBody(gch: var GcHeap) = @@ -850,13 +792,11 @@ when withRealTime: gch.maxPause = MaxPauseInUs.toNano proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = - acquire(gch) gch.maxPause = us.toNano if (gch.zct.len >= ZctThreshold or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or strongAdvice: collectCTBody(gch) - release(gch) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = if stackSize >= 0: @@ -880,18 +820,12 @@ when withRealTime: when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - discard atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock <= 0: raise newException(AssertionError, "API usage error: GC_enable called but GC is already enabled") - when hasThreadSupport and hasSharedHeap: - discard atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -904,12 +838,10 @@ when not defined(useNimRtl): # set to the max value to suppress the cycle detector proc GC_fullCollect() = - acquire(gch) var oldThreshold = gch.cycleThreshold gch.cycleThreshold = 0 # forces cycle collection collectCT(gch) gch.cycleThreshold = oldThreshold - release(gch) proc GC_getStatistics(): string = result = "[GC] total memory: " & $(getTotalMem()) & "\n" & diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 283919503..522b2742b 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -112,14 +112,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - # Which color to use for new objects is tricky: When we're marking, # they have to be *white* so that everything is marked that is only # reachable from them. However, when we are sweeping, they have to @@ -204,10 +196,6 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes -proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = - # we MUST access gch as a global here, because this crosses DLL boundaries! - discard - proc nimGCref(p: pointer) {.compilerProc.} = let cell = usrToCell(p) markAsEscaped(cell) @@ -240,13 +228,8 @@ template markGrey(x: PCell) = x.setColor(rcGrey) add(gch.greyStack, x) -proc GC_addCycleRoot*[T](p: ref T) {.inline.} = - ## adds 'p' to the cycle candidate set for the cycle collector. It is - ## necessary if you used the 'acyclic' pragma for optimization - ## purposes and need to break cycles manually. - discard - -template asgnRefImpl = +proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = + # the code generator calls this proc! gcAssert(not isOnStack(dest), "asgnRef") # BUGFIX: first incRef then decRef! if src != nil: @@ -255,12 +238,8 @@ template asgnRefImpl = markGrey(s) dest[] = src -proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc! - asgnRefImpl() - -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - asgnRefImpl() +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = # unsureAsgnRef marks 'src' as grey only if dest is not on the @@ -396,7 +375,6 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = result = newSeq(typ, len) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -420,7 +398,6 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = when useCellIds: inc gch.idGenerator res.id = gch.idGenerator - release(gch) result = cellToUsr(res) when defined(memProfiler): nimProfile(newsize-oldsize) @@ -732,16 +709,10 @@ when withRealTime: when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - discard atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock > 0: - when hasThreadSupport and hasSharedHeap: - discard atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 96221b175..d8cb3e2d0 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -85,14 +85,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -276,7 +268,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 incTypeSize typ, size - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch, size + sizeof(Cell)) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) @@ -288,7 +279,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = res.filename = framePtr.prev.filename res.line = framePtr.prev.line res.refcount = 0 - release(gch) when withBitvectors: incl(gch.allocated, res) when useCellIds: inc gch.idGenerator @@ -333,7 +323,6 @@ when not defined(gcDestructors): when defined(memProfiler): nimProfile(size) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch, newsize + sizeof(Cell)) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -353,7 +342,6 @@ when not defined(gcDestructors): when useCellIds: inc gch.idGenerator res.id = gch.idGenerator - release(gch) result = cellToUsr(res) when defined(memProfiler): nimProfile(newsize-oldsize) @@ -503,18 +491,12 @@ proc collectCT(gch: var GcHeap; size: int) = when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock <= 0: raise newException(AssertionError, "API usage error: GC_enable called but GC is already enabled") - when hasThreadSupport and hasSharedHeap: - atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -530,12 +512,10 @@ when not defined(useNimRtl): gch.tracing = true proc GC_fullCollect() = - acquire(gch) var oldThreshold = gch.cycleThreshold gch.cycleThreshold = 0 # forces cycle collection collectCT(gch, 0) gch.cycleThreshold = oldThreshold - release(gch) proc GC_getStatistics(): string = result = "[GC] total memory: " & $getTotalMem() & "\n" & diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 06fded86b..8a1446944 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -339,8 +339,8 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc alloc(size: Natural): pointer = result = c_malloc(size) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 5ac0ca8b2..8be19e5b8 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -519,8 +519,11 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} = `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); """ of nkList: - for i in 0..n.len-1: - nimCopyAux(dest, src, n.sons[i]) + asm """ + for (var i = 0; i < `n`.sons.length; i++) { + nimCopyAux(`dest`, `src`, `n`.sons[i]); + } + """ of nkCase: asm """ `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 683e84f7d..89bc11d2c 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -170,8 +170,8 @@ when defined(boehmgc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -326,8 +326,8 @@ elif defined(gogc): writebarrierptr(dest, src) proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = writebarrierptr(dest, src) - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - writebarrierptr(dest, src) + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -416,8 +416,8 @@ elif defined(nogc) and defined(useMalloc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -473,8 +473,8 @@ elif defined(nogc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) var allocator {.rtlThreadVar.}: MemRegion instantiateForRegion(allocator) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 7cb25a252..fb231bbed 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -64,7 +64,9 @@ proc reprStrAux(result: var string, s: cstring, len: int) = proc reprStr(s: string): string {.compilerRtl.} = result = "" - if cast[pointer](s).isNil: + var sIsNil = false + asm """`sIsNil` = `s` === null""" + if sIsNil: # cast[pointer](s).isNil: # Handle nil strings here because they don't have a length field in js # TODO: check for null/undefined before generating call to length in js? # Also: c backend repr of a nil string is <pointer>"", but repr of an diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 34b1cc4f7..c8c6101d7 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -159,19 +159,13 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym = if m != nil and m.ast != nil: result = findNode(m.ast, trackPos) -proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; +proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph: ModuleGraph) = let conf = graph.config myLog("cmd: " & $cmd & ", file: " & file.string & ", dirtyFile: " & dirtyfile.string & "[" & $line & ":" & $col & "]") conf.ideCmd = cmd - if cmd == ideChk: - conf.structuredErrorHook = errorHook - conf.writelnHook = myLog - else: - conf.structuredErrorHook = nil - conf.writelnHook = myLog if cmd == ideUse and conf.suggestVersion != 0: graph.resetAllModules() var isKnownFile = true @@ -186,7 +180,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; if conf.suggestVersion == 1: graph.usageSym = nil if not isKnownFile: - graph.compileProject() + graph.compileProject(dirtyIdx) if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.isEmpty: discard "no need to recompile anything" @@ -195,7 +189,8 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph.markDirty dirtyIdx graph.markClientsDirty dirtyIdx if conf.ideCmd != ideMod: - graph.compileProject(modIdx) + if isKnownFile: + graph.compileProject(modIdx) if conf.ideCmd in {ideUse, ideDus}: let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym if u != nil: @@ -203,6 +198,16 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; else: localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos)) +proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; + graph: ModuleGraph) = + if cmd == ideChk: + graph.config.structuredErrorHook = errorHook + graph.config.writelnHook = myLog + else: + graph.config.structuredErrorHook = nil + graph.config.writelnHook = myLog + executeNoHooks(cmd, file, dirtyfile, line, col, graph) + proc executeEpc(cmd: IdeCmd, args: SexpNode; graph: ModuleGraph) = let @@ -613,4 +618,113 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = discard self.loadConfigsAndRunMainCommand(cache, conf) -handleCmdline(newIdentCache(), newConfigRef()) +when isMainModule: + handleCmdline(newIdentCache(), newConfigRef()) +else: + export Suggest + export IdeCmd + export AbsoluteFile + type NimSuggest* = ref object + graph: ModuleGraph + idle: int + cachedMsgs: CachedMsgs + + proc initNimSuggest*(project: string, nimPath: string = ""): NimSuggest = + var retval: ModuleGraph + proc mockCommand(graph: ModuleGraph) = + retval = graph + let conf = graph.config + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass + conf.cmd = cmdIdeTools + wantMainModule(conf) + + if not fileExists(conf.projectFull): + quit "cannot find file: " & conf.projectFull.string + + add(conf.searchPaths, conf.libpath) + + # do not stop after the first error: + conf.errorMax = high(int) + # do not print errors, but log them + conf.writelnHook = proc (s: string) = log(s) + conf.structuredErrorHook = nil + + # compile the project before showing any input so that we already + # can answer questions right away: + compileProject(graph) + + + proc mockCmdLine(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = + conf.suggestVersion = 0 + let a = unixToNativePath(project) + if dirExists(a) and not fileExists(a.addFileExt("nim")): + conf.projectName = findProjectNimFile(conf, a) + # don't make it worse, report the error the old way: + if conf.projectName.len == 0: conf.projectName = a + else: + conf.projectName = a + # if processArgument(pass, p, argsCount): break + let + cache = newIdentCache() + conf = newConfigRef() + self = NimProg( + suggestMode: true, + processCmdLine: mockCmdLine, + mainCommand: mockCommand + ) + self.initDefinesProg(conf, "nimsuggest") + + self.processCmdLineAndProjectPath(conf) + + if gMode != mstdin: + conf.writelnHook = proc (msg: string) = discard + # Find Nim's prefix dir. + if nimPath == "": + let binaryPath = findExe("nim") + if binaryPath == "": + raise newException(IOError, + "Cannot find Nim standard library: Nim compiler not in PATH") + conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() + if not dirExists(conf.prefixDir / RelativeDir"lib"): + conf.prefixDir = AbsoluteDir"" + else: + conf.prefixDir = AbsoluteDir nimPath + + #msgs.writelnHook = proc (line: string) = log(line) + myLog("START " & conf.projectFull.string) + + discard self.loadConfigsAndRunMainCommand(cache, conf) + if gLogging: + for it in conf.searchPaths: + log(it.string) + + retval.doStopCompile = proc (): bool = false + return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[]) + + proc runCmd*(nimsuggest: NimSuggest, cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int): seq[Suggest] = + var retval: seq[Suggest] = @[] + let conf = nimsuggest.graph.config + conf.ideCmd = cmd + conf.writelnHook = proc (line: string) = + retval.add(Suggest(section: ideMsg, doc: line)) + conf.suggestionResultHook = proc (s: Suggest) = + retval.add(s) + conf.writelnHook = proc (s: string) = + stderr.write s & "\n" + if conf.ideCmd == ideKnown: + retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file)))) + else: + if conf.ideCmd == ideChk: + for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) + if conf.ideCmd == ideChk: + conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) = + retval.add(Suggest(section: ideChk, filePath: toFullPath(conf, info), + line: toLinenumber(info), column: toColumn(info), doc: msg, + forth: $sev)) + + else: + conf.structuredErrorHook = nil + executeNoHooks(conf.ideCmd, file, dirtyfile, line, col, nimsuggest.graph) + return retval diff --git a/testament/categories.nim b/testament/categories.nim index 36f2a271a..e1f173c26 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -97,11 +97,10 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = else: "" - testSpec c, makeTest("lib/nimrtl.nim", - options & " --app:lib -d:createNimRtl --threads:on", cat) - testSpec c, makeTest("tests/dll/server.nim", - options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat) - + testNoSpec c, makeTest("lib/nimrtl.nim", + options & " --app:lib -d:createNimRtl --threads:on", cat, actionCompile) + testNoSpec c, makeTest("tests/dll/server.nim", + options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat, actionCompile) when defined(Windows): # windows looks in the dir of the exe (yay!): @@ -298,25 +297,24 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = # whoever edits these hashes without dom96's permission, j/k. But please only # edit when making a conscious breaking change, also please try to make your # commit message clear and notify me so I can easily compile an errata later. - var testHashes: seq[string] = @[] - - for test in tests: - testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string)) - const refHashes = @[ "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", - "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b", - "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184", - "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770", - "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e", - "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02", - "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8", - "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127" + "13febc363ed82585f2a60de40ddfefda", "c11a013db35e798f44077bc0763cc86d", + "3e32e2c5e9a24bd13375e1cd0467079c", "0b9fe7ba159623d49ae60db18a15037c", + "b2dd5293d7f784824bbf9792c6fb51ad", "4c19d8d9026bfe151b31d7007fa3c237", + "9415c6a568cfceed08da8378e95b5cd5", "da520038c153f4054cb8cc5faa617714", + "e6c6e061b6f77b2475db6fec7abfb7f4", "9a8fe78c588d08018843b64b57409a02", + "8b5d28e985c0542163927d253a3e4fc9", "783299b98179cc725f9c46b5e3b5381f", + "bc523f9a9921299090bac1af6c958e73", "80f9c3e594a798225046e8a42e990daf" ] - doAssert testHashes == refHashes, "Nim in Action tests were changed." + + for i, test in tests: + let filename = "tests" / test.addFileExt("nim") + let testHash = getMD5(readFile(filename).string) + doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed." # Run the tests. for testfile in tests: @@ -329,8 +327,6 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testCPP cppFile - - # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: diff --git a/testament/specs.nim b/testament/specs.nim index c51a3343e..86fc8bed4 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -10,20 +10,21 @@ import parseutils, strutils, os, osproc, streams, parsecfg -var compilerPrefix* = "compiler" / "nim " +var compilerPrefix* = "compiler" / "nim" let isTravis* = existsEnv("TRAVIS") let isAppVeyor* = existsEnv("APPVEYOR") proc cmdTemplate*(): string = - compilerPrefix & "$target --lib:lib --hints:on -d:testing --nimblePath:tests/deps $options $file" + compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file" type TTestAction* = enum - actionCompile = "compile" actionRun = "run" + actionCompile = "compile" actionReject = "reject" actionRunNoSpec = "runNoSpec" + TResultEnum* = enum reNimcCrash, # nim compiler seems to have crashed reMsgsDiffer, # error messages differ @@ -39,6 +40,7 @@ type reBuildFailed # package building failed reIgnored, # test is ignored reSuccess # test was successful + TTarget* = enum targetC = "C" targetCpp = "C++" @@ -48,6 +50,7 @@ type TSpec* = object action*: TTestAction file*, cmd*: string + input*: string outp*: string line*, column*: int tfile*: string @@ -144,6 +147,8 @@ proc parseSpec*(filename: string): TSpec = of "output": result.action = actionRun result.outp = e.value + of "input": + result.input = e.value of "outputsub": result.action = actionRun result.outp = e.value @@ -186,7 +191,7 @@ proc parseSpec*(filename: string): TSpec = raise newException(ValueError, "cannot interpret as a bool: " & e.value) of "cmd": if e.value.startsWith("nim "): - result.cmd = compilerPrefix & e.value[4..^1] + result.cmd = compilerPrefix & e.value[3..^1] else: result.cmd = e.value of "ccodecheck": result.ccodeCheck = e.value diff --git a/testament/tester.nim b/testament/tester.nim index 169210255..59c0171b4 100644 --- a/testament/tester.nim +++ b/testament/tester.nim @@ -14,6 +14,8 @@ import marshal, backend, parseopt, specs, htmlgen, browsers, terminal, algorithm, compiler/nodejs, times, sets, md5 +var useColors = true + const resultsFile = "testresults.html" #jsonFile = "testresults.json" # not used @@ -32,6 +34,8 @@ Options: --failing only show failing/ignored tests --targets:"c c++ js objc" run tests for specified targets (default: all) --nim:path use a particular nim executable (default: compiler/nim) + --directory:dir Change to directory dir before reading the tests or doing anything else. + --colors:on|off turn messagescoloring on|off """ % resultsFile type @@ -74,6 +78,33 @@ proc getFileDir(filename: string): string = if not result.isAbsolute(): result = getCurrentDir() / result +proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string): tuple[ + output: TaintedString, + exitCode: int] {.tags: + [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = + var p = startProcess(command, args=args, options=options) + var outp = outputStream(p) + + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + let instream = inputStream(p) + instream.write(input) + close instream + + result = (TaintedString"", -1) + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + result[0].string.add(line.string) + result[0].string.add("\n") + else: + result[1] = peekExitCode(p) + if result[1] != -1: break + close(p) + + + proc nimcacheDir(filename, options: string, target: TTarget): string = ## Give each test a private nimcache dir so they don't clobber each other's. let hashInput = options & $target @@ -153,11 +184,30 @@ proc initResults: TResults = result.skipped = 0 result.data = "" -#proc readResults(filename: string): TResults = # not used -# result = marshal.to[TResults](readFile(filename).string) +import macros + +macro ignoreStyleEcho(args: varargs[typed]): untyped = + let typForegroundColor = bindSym"ForegroundColor".getType + let typBackgroundColor = bindSym"BackgroundColor".getType + let typStyle = bindSym"Style".getType + let typTerminalCmd = bindSym"TerminalCmd".getType + result = newCall(bindSym"echo") + for arg in children(args): + if arg.kind == nnkNilLit: continue + let typ = arg.getType + if typ.kind != nnkEnumTy or + typ != typForegroundColor and + typ != typBackgroundColor and + typ != typStyle and + typ != typTerminalCmd: + result.add(arg) + +template maybeStyledEcho(args: varargs[untyped]): untyped = + if useColors: + styledEcho(args) + else: + ignoreStyleEcho(args) -#proc writeResults(filename: string, r: TResults) = # not used -# writeFile(filename, $$r) proc `$`(x: TResults): string = result = ("Tests passed: $1 / $3 <br />\n" & @@ -168,7 +218,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, expected, given: string, success: TResultEnum) = let name = test.name.extractFilename & " " & $target & test.options let duration = epochTime() - test.startTime - let durationStr = duration.formatFloat(ffDecimal, precision = 8) + let durationStr = duration.formatFloat(ffDecimal, precision = 8).align(11) backend.writeTestResult(name = name, category = test.cat.string, target = $target, @@ -178,17 +228,17 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) if success == reSuccess: - styledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " secs)" + maybeStyledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " secs)" elif success == reIgnored: - styledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name + maybeStyledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name else: - styledEcho styleBright, fgRed, "FAIL: ", fgCyan, name - styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" - styledEcho styleBright, fgRed, "Failure: ", $success - styledEcho fgYellow, "Expected:" - styledEcho styleBright, expected, "\n" - styledEcho fgYellow, "Gotten:" - styledEcho styleBright, given, "\n" + maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name + maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" + maybeStyledEcho styleBright, fgRed, "Failure: ", $success + maybeStyledEcho fgYellow, "Expected:" + maybeStyledEcho styleBright, expected, "\n" + maybeStyledEcho fgYellow, "Gotten:" + maybeStyledEcho styleBright, given, "\n" if existsEnv("APPVEYOR"): let (outcome, msg) = @@ -268,10 +318,15 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st echo getCurrentExceptionMsg() proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = - let exp = expectedNimout.strip.replace("\C\L", "\L") - let giv = given.nimout.strip.replace("\C\L", "\L") - if exp notin giv: - given.err = reMsgsDiffer + let giv = given.nimout.strip + var currentPos = 0 + # Only check that nimout contains all expected lines in that order. + # There may be more output in nimout. It is ignored here. + for line in expectedNimout.strip.splitLines: + currentPos = giv.find(line.strip, currentPos) + if currentPos < 0: + given.err = reMsgsDiffer + return proc makeDeterministic(s: string): string = var x = splitLines(s) @@ -297,7 +352,6 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, proc testSpec(r: var TResults, test: TTest, target = targetC) = let tname = test.name.addFileExt(".nim") - #echo "TESTING ", tname var expected: TSpec if test.action != actionRunNoSpec: expected = parseSpec(tname) @@ -336,6 +390,12 @@ proc testSpec(r: var TResults, test: TTest, target = targetC) = var given = callCompiler(expected.cmd, test.name, test.options, target) + # echo "expected.cmd: ", expected.cmd + # echo "nimout: ", given.nimout + # echo "outp: ", given.outp + # echo "msg: ", given.msg + # echo "err: ", given.err + if given.err != reSuccess: r.addResult(test, target, "", given.msg, given.err) continue @@ -358,8 +418,16 @@ proc testSpec(r: var TResults, test: TTest, target = targetC) = reExeNotFound) continue - let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile - var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut}) + + var exeCmd: string + var args: seq[string] + if isJsTarget: + exeCmd = nodejs + args.add exeFile + else: + exeCmd = exeFile + + var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input) # Treat all failure codes from nodejs as 1. Older versions of nodejs used # to return other codes, but for us it is sufficient to know that it's not 0. @@ -402,7 +470,7 @@ proc testC(r: var TResults, test: TTest) = # runs C code. Doesn't support any specs, just goes by exit code. let tname = test.name.addFileExt(".c") inc(r.total) - styledEcho "Processing ", fgCyan, extractFilename(tname) + maybeStyledEcho "Processing ", fgCyan, extractFilename(tname) var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, targetC) if given.err != reSuccess: r.addResult(test, targetC, "", given.msg, given.err) @@ -459,6 +527,7 @@ proc main() = var targetsStr = "" + var p = initOptParser() p.next() while p.kind == cmdLongoption: @@ -469,8 +538,18 @@ proc main() = of "targets": targetsStr = p.val.string targets = parseTargets(targetsStr) - of "nim": compilerPrefix = p.val.string & " " - else: quit Usage + of "nim": compilerPrefix = p.val.string + of "directory": + setCurrentDir(p.val.string) + of "colors": + if p.val.string == "on": + useColors = true + elif p.val.string == "off": + useColors = false + else: + quit Usage + else: + quit Usage p.next() if p.kind != cmdArgument: quit Usage var action = p.key.string.normalize diff --git a/testament/tests/shouldfail/tccodecheck.nim b/testament/tests/shouldfail/tccodecheck.nim new file mode 100644 index 000000000..a8d216a5b --- /dev/null +++ b/testament/tests/shouldfail/tccodecheck.nim @@ -0,0 +1,8 @@ +discard """ +ccodecheck: "baz" +""" + +proc foo(): void {.exportc: "bar".}= + echo "Hello World" + +foo() diff --git a/testament/tests/shouldfail/tcolumn.nim b/testament/tests/shouldfail/tcolumn.nim new file mode 100644 index 000000000..f4046d58d --- /dev/null +++ b/testament/tests/shouldfail/tcolumn.nim @@ -0,0 +1,8 @@ +discard """ +line: 8 +column: 7 +errormsg: "undeclared identifier: 'undeclared'" +""" + +# test should fail because the line directive is wrong +echo undeclared diff --git a/testament/tests/shouldfail/terrormsg.nim b/testament/tests/shouldfail/terrormsg.nim new file mode 100644 index 000000000..61c08d93d --- /dev/null +++ b/testament/tests/shouldfail/terrormsg.nim @@ -0,0 +1,8 @@ +discard """ +line: 8 +column: 6 +errormsg: "wrong error message" +""" + +# test should fail because the line directive is wrong +echo undeclared diff --git a/testament/tests/shouldfail/texitcode1.nim b/testament/tests/shouldfail/texitcode1.nim new file mode 100644 index 000000000..1b38b4f2e --- /dev/null +++ b/testament/tests/shouldfail/texitcode1.nim @@ -0,0 +1,3 @@ +discard """ +exitcode: 1 +""" diff --git a/testament/tests/shouldfail/tfile.nim b/testament/tests/shouldfail/tfile.nim new file mode 100644 index 000000000..07a526c68 --- /dev/null +++ b/testament/tests/shouldfail/tfile.nim @@ -0,0 +1,6 @@ +discard """ +file: "notthisfile.nim" +errmsg: "undeclared identifier: 'undefined'" +""" + +echo undefined diff --git a/testament/tests/shouldfail/tline.nim b/testament/tests/shouldfail/tline.nim new file mode 100644 index 000000000..963e44fc7 --- /dev/null +++ b/testament/tests/shouldfail/tline.nim @@ -0,0 +1,8 @@ +discard """ +line: 9 +column: 6 +errormsg: "undeclared identifier: 'undeclared'" +""" + +# test should fail because the line directive is wrong +echo undeclared diff --git a/testament/tests/shouldfail/tmaxcodesize.nim b/testament/tests/shouldfail/tmaxcodesize.nim new file mode 100644 index 000000000..9879e4181 --- /dev/null +++ b/testament/tests/shouldfail/tmaxcodesize.nim @@ -0,0 +1,5 @@ +discard """ +maxcodesize: 1 +""" + +echo "Hello World" diff --git a/testament/tests/shouldfail/tmsg.nim b/testament/tests/shouldfail/tmsg.nim new file mode 100644 index 000000000..4ad17fa95 --- /dev/null +++ b/testament/tests/shouldfail/tmsg.nim @@ -0,0 +1,6 @@ +discard """ +msg: "Hello World" +""" + +static: + echo "something else" diff --git a/testament/tests/shouldfail/tnimout.nim b/testament/tests/shouldfail/tnimout.nim new file mode 100644 index 000000000..c0e332053 --- /dev/null +++ b/testament/tests/shouldfail/tnimout.nim @@ -0,0 +1,7 @@ +discard """ +nimout: "Hello World!" +action: compile +""" + +static: + echo "something else" diff --git a/testament/tests/shouldfail/toutput.nim b/testament/tests/shouldfail/toutput.nim new file mode 100644 index 000000000..ac0bc7a46 --- /dev/null +++ b/testament/tests/shouldfail/toutput.nim @@ -0,0 +1,7 @@ +discard """ +output: ''' +done +''' +""" + +echo "broken" diff --git a/testament/tests/shouldfail/toutputsub.nim b/testament/tests/shouldfail/toutputsub.nim new file mode 100644 index 000000000..7cc51ee8d --- /dev/null +++ b/testament/tests/shouldfail/toutputsub.nim @@ -0,0 +1,5 @@ +discard """ +outputsub: "something else" +""" + +echo "Hello World!" diff --git a/testament/tests/shouldfail/tsortoutput.nim b/testament/tests/shouldfail/tsortoutput.nim new file mode 100644 index 000000000..4ce9ce26d --- /dev/null +++ b/testament/tests/shouldfail/tsortoutput.nim @@ -0,0 +1,11 @@ +discard """ +sortoutput: true +output: ''' +2 +1 +''' +""" + +# this test should ensure that the output is actually sorted +echo "2" +echo "1" diff --git a/tests/async/tasyncall.nim b/tests/async/tasyncall.nim index 775dd0c6f..3e30e8ba8 100644 --- a/tests/async/tasyncall.nim +++ b/tests/async/tasyncall.nim @@ -2,7 +2,7 @@ discard """ file: "tasyncall.nim" exitcode: 0 """ -import times, sequtils, unittest +import times, sequtils import asyncdispatch const diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index 61f0b5661..212260922 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -3,7 +3,12 @@ discard """ cmd: "nim $target --hints:on --define:ssl $options $file" output: "500" disabled: "windows" + target: c + action: compile """ + +# XXX, deactivated + import asyncdispatch, asyncnet, net, strutils, os when defined(ssl): @@ -63,5 +68,3 @@ when defined(ssl): assert msgCount == swarmSize * messagesToSend echo msgCount - - diff --git a/tests/async/tfuturevar.nim b/tests/async/tfuturevar.nim index ea2c63e03..9e3134261 100644 --- a/tests/async/tfuturevar.nim +++ b/tests/async/tfuturevar.nim @@ -1,3 +1,8 @@ +discard """ +action: compile +""" +# XXX: action should be run! + import asyncdispatch proc completeOnReturn(fut: FutureVar[string], x: bool) {.async.} = @@ -44,4 +49,3 @@ proc main() {.async.} = waitFor main() - diff --git a/tests/casestmt/tlinearscanend.nim b/tests/casestmt/tlinearscanend.nim index 9a984e039..96e3727d5 100644 --- a/tests/casestmt/tlinearscanend.nim +++ b/tests/casestmt/tlinearscanend.nim @@ -1,3 +1,6 @@ +discard """ +action: compile +""" import strutils @@ -21,4 +24,3 @@ of 21: echo "21" else: {.linearScanEnd.} echo "default" - diff --git a/tests/ccgbugs/t6756.nim b/tests/ccgbugs/t6756.nim index 0f08557eb..5170a99f4 100644 --- a/tests/ccgbugs/t6756.nim +++ b/tests/ccgbugs/t6756.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +(v: 3) +''' +""" + import typetraits type A[T] = ref object diff --git a/tests/ccgbugs/t8964.nim b/tests/ccgbugs/t8964.nim deleted file mode 100644 index 5b41e8bdb..000000000 --- a/tests/ccgbugs/t8964.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - targets: "c cpp" -""" - -from json import JsonParsingError -import marshal - -const nothing = "" -doAssertRaises(JsonParsingError): - var bar = marshal.to[int](nothing) diff --git a/tests/ccgbugs/tcodegendecllambda.nim b/tests/ccgbugs/tcodegendecllambda.nim index 6dce68db5..5c6608b77 100644 --- a/tests/ccgbugs/tcodegendecllambda.nim +++ b/tests/ccgbugs/tcodegendecllambda.nim @@ -1,6 +1,7 @@ discard """ targets: "c cpp js" ccodecheck: "'HELLO'" + action: compile """ when defined(JS): diff --git a/tests/ccgbugs/tgeneric_closure.nim b/tests/ccgbugs/tgeneric_closure.nim index f9d5e7910..bb3b924b9 100644 --- a/tests/ccgbugs/tgeneric_closure.nim +++ b/tests/ccgbugs/tgeneric_closure.nim @@ -1,4 +1,10 @@ - +discard """ +output: ''' +2 +2 +2 +''' +""" # bug 2659 diff --git a/tests/ccgbugs/trecursive_closure.nim b/tests/ccgbugs/trecursive_closure.nim index f64382a8c..4b6514b90 100644 --- a/tests/ccgbugs/trecursive_closure.nim +++ b/tests/ccgbugs/trecursive_closure.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # bug #2233 type MalType = object fun: proc: MalType diff --git a/tests/ccgbugs/tsighash_typename_regression.nim b/tests/ccgbugs/tsighash_typename_regression.nim index 7122902d9..6e49bafc3 100644 --- a/tests/ccgbugs/tsighash_typename_regression.nim +++ b/tests/ccgbugs/tsighash_typename_regression.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +123 +baz +''' +""" + # bug #5147 proc foo[T](t: T) = diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim index 7e9e91836..671986054 100644 --- a/tests/ccgbugs/tuple_canon.nim +++ b/tests/ccgbugs/tuple_canon.nim @@ -1,4 +1,9 @@ - +discard """ +output: ''' +vidx 18 +0,0 +''' +""" # bug #4626 var foo: (int, array[1, int]) # Tuple must be of length > 1 diff --git a/tests/closure/tinfer_closure_for_nestedproc.nim b/tests/closure/tinfer_closure_for_nestedproc.nim new file mode 100644 index 000000000..6450d1492 --- /dev/null +++ b/tests/closure/tinfer_closure_for_nestedproc.nim @@ -0,0 +1,42 @@ +discard """ + action: compile +""" + +# bug #9441 +import asyncdispatch, asyncfutures, strtabs + +type + Request = object + Context = object + position: int + accept: bool + headers: StringTableRef + Handler = proc (r: ref Request, c: Context): Future[Context] + +proc respond(req: Request): Future[void] = discard + +proc handle*(h: Handler): auto = # (proc (req: Request): Future[void]) = + proc server(req: Request): Future[void] {.async.} = + let emptyCtx = Context( + position: 0, + accept: true, + headers: newStringTable() + ) + var reqHeap = new(Request) + reqHeap[] = req + var + f: Future[Context] + ctx: Context + try: + f = h(reqHeap, emptyCtx) + ctx = await f + except: + discard + if f.failed: + await req.respond() + else: + if not ctx.accept: + await req.respond() + return server + +waitFor handle(nil)(Request()) diff --git a/tests/closure/tmacrobust1512.nim b/tests/closure/tmacrobust1512.nim index 5f13e8286..0f44c5e1a 100644 --- a/tests/closure/tmacrobust1512.nim +++ b/tests/closure/tmacrobust1512.nim @@ -1,8 +1,12 @@ +discard """ +output: "" +""" + import macros, strutils # https://github.com/nim-lang/Nim/issues/1512 -proc macrobust0(raw_input: string) = +proc macrobust0(input: string): string = var output = "" proc p1(a:string) = output.add(a) @@ -27,13 +31,9 @@ proc macrobust0(raw_input: string) = proc p19(a:string) = p18(a) proc p20(a:string) = p19(a) - let input = $raw_input - for a in input.split(): p20(a) p19(a) - - p18(a) p17(a) p16(a) @@ -53,11 +53,9 @@ proc macrobust0(raw_input: string) = p2(a) p1(a) + result = output - echo output - -macro macrobust(raw_input: untyped): untyped = - +macro macrobust(input: static[string]): untyped = var output = "" proc p1(a:string) = output.add(a) @@ -82,12 +80,9 @@ macro macrobust(raw_input: untyped): untyped = proc p19(a:string) = p18(a) proc p20(a:string) = p19(a) - let input = $raw_input - for a in input.split(): p20(a) p19(a) - p18(a) p17(a) p16(a) @@ -105,11 +100,11 @@ macro macrobust(raw_input: untyped): untyped = p4(a) p3(a) p2(a) + p1(a) - echo output - discard result + result = newLit(output) -macrobust """ +const input = """ fdsasadfsdfa sadfsdafsdaf dsfsdafdsfadsfa fsdaasdfasdf fsdafsadfsad asdfasdfasdf @@ -122,16 +117,7 @@ macrobust """ sdfasdafsadf sdfasdafsdaf sdfasdafsdaf """ +let str1 = macrobust(input) +let str2 = macrobust0(input) -macrobust0 """ - fdsasadfsdfa sadfsdafsdaf - dsfsdafdsfadsfa fsdaasdfasdf - fsdafsadfsad asdfasdfasdf - fdsasdfasdfa sadfsadfsadf - sadfasdfsdaf sadfsdafsdaf dsfasdaf - sadfsdafsadf fdsasdafsadf fdsasadfsdaf - sdfasadfsdafdfsa sadfsadfsdaf - sdafsdaffsda sdfasadfsadf - fsdasdafsdfa sdfasdfafsda - sdfasdafsadf sdfasdafsdaf sdfasdafsdaf -""" +doAssert str1 == str2 diff --git a/tests/closure/ttimeinfo.nim b/tests/closure/ttimeinfo.nim index 3096a5d65..7416c0d31 100644 --- a/tests/closure/ttimeinfo.nim +++ b/tests/closure/ttimeinfo.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +@[2000-01-01T00:00:00+00:00, 2001-01-01T00:00:00+00:00, 2002-01-01T00:00:00+00:00, 2003-01-01T00:00:00+00:00, 2004-01-01T00:00:00+00:00, 2005-01-01T00:00:00+00:00, 2006-01-01T00:00:00+00:00, 2007-01-01T00:00:00+00:00, 2008-01-01T00:00:00+00:00, 2009-01-01T00:00:00+00:00, 2010-01-01T00:00:00+00:00, 2011-01-01T00:00:00+00:00, 2012-01-01T00:00:00+00:00, 2013-01-01T00:00:00+00:00, 2014-01-01T00:00:00+00:00, 2015-01-01T00:00:00+00:00] +@[2000-01-01T00:00:00+00:00, 2001-01-01T00:00:00+00:00, 2002-01-01T00:00:00+00:00, 2003-01-01T00:00:00+00:00, 2004-01-01T00:00:00+00:00, 2005-01-01T00:00:00+00:00, 2006-01-01T00:00:00+00:00, 2007-01-01T00:00:00+00:00, 2008-01-01T00:00:00+00:00, 2009-01-01T00:00:00+00:00, 2010-01-01T00:00:00+00:00, 2011-01-01T00:00:00+00:00, 2012-01-01T00:00:00+00:00, 2013-01-01T00:00:00+00:00, 2014-01-01T00:00:00+00:00, 2015-01-01T00:00:00+00:00] +''' +""" + # bug #2073 import sequtils diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim index 9c7fd10b3..c435ebaac 100644 --- a/tests/compiles/trecursive_generic_in_compiles.nim +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # bug #3313 import unittest, sugar {.experimental: "notnil".} diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index e145b9f37..df4037ffb 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -1,5 +1,4 @@ discard """ - file: "tissues.nim" output: ''' 20.0 USD Printable diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim index 5082fcb61..6b959eff2 100644 --- a/tests/concepts/tmapconcept.nim +++ b/tests/concepts/tmapconcept.nim @@ -3,7 +3,7 @@ output: '''10 10 1''' -msg: ''' +nimout: ''' K=string V=int K=int64 V=string K=int V=int diff --git a/tests/concepts/tmatrixconcept.nim b/tests/concepts/tmatrixconcept.nim index dd5a080b6..ca31f5942 100644 --- a/tests/concepts/tmatrixconcept.nim +++ b/tests/concepts/tmatrixconcept.nim @@ -1,6 +1,6 @@ discard """ output: "0\n0\n0" -msg: ''' +nimout: ''' R=3 C=3 TE=9 FF=14 FC=20 T=int R=3 C=3 T=int ''' diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim index cb8db566d..fb6032732 100644 --- a/tests/concepts/tstackconcept.nim +++ b/tests/concepts/tstackconcept.nim @@ -1,6 +1,6 @@ discard """ output: "20\n10" -msg: ''' +nimout: ''' INFERRED int VALUE TYPE int VALUE TYPE NAME INT diff --git a/tests/controlflow/tstatret.nim b/tests/controlflow/tstatret.nim index 04cac9966..8f43c5d8f 100644 --- a/tests/controlflow/tstatret.nim +++ b/tests/controlflow/tstatret.nim @@ -1,7 +1,7 @@ discard """ file: "tstatret.nim" line: 9 - errormsg: "unreachable statement after 'return'" + errormsg: "unreachable statement after 'return' statement or '{.noReturn.}' proc" """ # no statement after return proc main() = diff --git a/tests/converter/t7098.nim b/tests/converter/t7098.nim index 66e629fa8..8e7634882 100644 --- a/tests/converter/t7098.nim +++ b/tests/converter/t7098.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + type Byte* = uint8 Bytes* = seq[Byte] diff --git a/tests/converter/tconvcolors.nim b/tests/converter/tconvcolors.nim index 07e829550..7b440cabd 100644 --- a/tests/converter/tconvcolors.nim +++ b/tests/converter/tconvcolors.nim @@ -1,5 +1,7 @@ +discard """ +output: "16777215A" +""" import colors echo int32(colWhite), 'A' - diff --git a/tests/converter/tconvert.nim b/tests/converter/tconvert.nim index 8aa4e97a3..5eee2a92d 100644 --- a/tests/converter/tconvert.nim +++ b/tests/converter/tconvert.nim @@ -26,3 +26,19 @@ block: var x = "101" var y: int = x # instantiate withVar doAssert(y == ord('0')) + + +###################### +# bug #3503 +type Foo = object + r: float + +converter toFoo(r: float): Foo = + result.r = r + +proc `+=`*(x: var Foo, r: float) = + x.r += r + +var a: Foo +a.r += 3.0 + diff --git a/tests/converter/tconverter_unique_ptr.nim b/tests/converter/tconverter_unique_ptr.nim index 15ec609a3..25b001d52 100644 --- a/tests/converter/tconverter_unique_ptr.nim +++ b/tests/converter/tconverter_unique_ptr.nim @@ -2,9 +2,7 @@ discard """ file: "tconverter_unique_ptr.nim" targets: "c cpp" - output: '''5 -2.0 5 -''' + output: "" """ ## Bugs 9698 and 9699 @@ -22,6 +20,8 @@ type data: ptr UncheckedArray[float] proc `$`(x: MyLen): string {.borrow.} +proc `==`(x1, x2: MyLen): bool {.borrow.} + proc `=destroy`*(m: var MySeq) {.inline.} = if m.data != nil: @@ -50,10 +50,12 @@ proc len*(m: MySeq): MyLen {.inline.} = m.len proc lenx*(m: var MySeq): MyLen {.inline.} = m.len - proc `[]`*(m: MySeq; i: MyLen): float {.inline.} = m.data[i.int] +proc `[]`*(m: var MySeq; i: MyLen): var float {.inline.} = + m.data[i.int] + proc `[]=`*(m: var MySeq; i: MyLen, val: float) {.inline.} = m.data[i.int] = val @@ -97,11 +99,55 @@ proc newUniquePtr*[T](val: sink T): UniquePtr[T] = converter convertPtrToObj*[T](p: UniquePtr[T]): var T = result = p.val[] - var pu = newUniquePtr(newMySeq(5, 1.0)) -echo pu.len +let pu2 = newUniquePtr(newMySeq(5, 1.0)) +doAssert: pu.len == 5 +doAssert: pu2.len == 5 +doAssert: pu.lenx == 5 +doAssert: pu2.lenx == 5 pu[0] = 2.0 -echo pu[0], " ", pu.lenx +pu2[0] = 2.0 +doAssert pu[0] == 2.0 +doAssert: pu2[0] == 2.0 + +##----------------------------------------------------------------------------------------- +## Bugs #9735 and #9736 +type + ConstPtr*[T] = object + ## This pointer makes it impossible to change underlying value + ## as it returns only `lent T` + val: ptr T +proc `=destroy`*[T](p: var ConstPtr[T]) = + if p.val != nil: + `=destroy`(p.val[]) + dealloc(p.val) + p.val = nil + +proc `=`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.} + +proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} = + if dest.val != nil and dest.val != src.val: + `=destroy`(dest) + dest.val = src.val + +proc newConstPtr*[T](val: sink T): ConstPtr[T] = + result.val = cast[type(result.val)](alloc(sizeof(result.val[]))) + reset(result.val[]) + result.val[] = val + +converter convertConstPtrToObj*[T](p: ConstPtr[T]): lent T = + result = p.val[] +var pc = newConstPtr(newMySeq(3, 1.0)) +let pc2 = newConstPtr(newMySeq(3, 1.0)) +doAssert: pc.len == 3 +doAssert: pc.len == 3 +doAssert: compiles(pc.lenx == 2) == false +doAssert: compiles(pc2.lenx == 2) == false +doAssert: compiles(pc[0] = 2.0) == false +doAssert: compiles(pc2[0] = 2.0) == false + +doAssert: pc[0] == 1.0 +doAssert: pc2[0] == 1.0 diff --git a/tests/converter/tgenericconverter2.nim b/tests/converter/tgenericconverter2.nim index ae064d852..017651a6b 100644 --- a/tests/converter/tgenericconverter2.nim +++ b/tests/converter/tgenericconverter2.nim @@ -1,4 +1,36 @@ # bug #3799 +discard """ +output: ''' +00000000000000000000000000000000000000000 +00000000000001111111111111110000000000000 +00000000001111111111111111111110000000000 +00000000111111111111111111111111100000000 +00000011111222222221111111111111111000000 +00000111122222222222221111111111111100000 +00001112222333333459432111111111111110000 +00011122355544344463533221111111111111000 +00111124676667556896443322211111111111100 +00111126545561919686543322221111111111100 +01111123333346967807554322222211111111110 +01111122233334455582015332222221111111110 +01111122222333344567275432222222111111110 +01111112222222334456075443222222211111110 +01111111222222233459965444332222221111110 +01111111122222223457486554433322222111110 +01111111112222222367899655543333322111110 +01111111111122222344573948465444332111110 +00111111111112222334467987727667762111100 +00111111111111122233474655557836432111100 +00011111111111112233 454433334 4321111000 +00001111111111111122354333322222211110000 +00000111111111111111222222222222111100000 +00000001111111111111111122222111110000000 +00000000111111111111111111111111100000000 +00000000000111111111111111111100000000000 +00000000000000111111111111100000000000000 +''' +""" + import macros @@ -37,9 +69,9 @@ iterator stepIt[T](start, step: T, iterations: int): T = let c = (0.36237, 0.32) -for y in stepIt(2.0, -0.0375, 107): +for y in stepIt(2.0, -0.0375 * 4, 107 div 4): var row = "" - for x in stepIt(-2.0, 0.025, 160): + for x in stepIt(-2.0, 0.025 * 4, 160 div 4): #let n = julia((x, y), c, 4.0, nmax) ### this works let n = dendriteFractal((x, y), 4.0, nmax) if n < nmax: diff --git a/tests/coroutines/texceptions.nim b/tests/coroutines/texceptions.nim index e67f954c3..323eb055d 100644 --- a/tests/coroutines/texceptions.nim +++ b/tests/coroutines/texceptions.nim @@ -14,6 +14,7 @@ proc testExceptions(id: int, sleep: float) = numbers.add(id) raise (ref ValueError)() except: + suspend(sleep) numbers.add(id) suspend(sleep) numbers.add(id) @@ -22,6 +23,6 @@ proc testExceptions(id: int, sleep: float) = start(proc() = testExceptions(1, 0.01)) start(proc() = testExceptions(2, 0.011)) -run() +coro.run() doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted") doAssert(numbers == @[1, 2, 1, 2, 1, 2, 1, 2, 1, 2], "Coroutines executed in incorrect order") diff --git a/tests/coroutines/titerators.nim b/tests/coroutines/titerators.nim index 610f2771a..abcfbde43 100644 --- a/tests/coroutines/titerators.nim +++ b/tests/coroutines/titerators.nim @@ -22,7 +22,7 @@ var start = getTicks() start(proc() = theCoroutine(1, 0.01)) start(proc() = theCoroutine(2, 0.011)) run() + var executionTime = getTicks() - start -doAssert(executionTime >= 55_000_000.Nanos and executionTime < 56_000_000.Nanos, "Coroutines executed too short") doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted") doAssert(numbers == @[10, 20, 11, 21, 12, 22, 13, 23, 14, 24], "Coroutines executed in incorrect order") diff --git a/tests/cpp/t8241.nim b/tests/cpp/t8241.nim index cbee1d85a..9aed13fcb 100644 --- a/tests/cpp/t8241.nim +++ b/tests/cpp/t8241.nim @@ -20,4 +20,13 @@ proc findlib2: string = proc imported_func2*(a: cint): cstring {.importc, dynlib: findlib2().} echo imported_func(1) -echo imported_func2(1) \ No newline at end of file +echo imported_func2(1) + +# issue #8946 + +from json import JsonParsingError +import marshal + +const nothing = "" +doAssertRaises(JsonParsingError): + var bar = marshal.to[int](nothing) diff --git a/tests/cpp/tnativesockets.nim b/tests/cpp/tnativesockets.nim index c62008050..1284811b5 100644 --- a/tests/cpp/tnativesockets.nim +++ b/tests/cpp/tnativesockets.nim @@ -1,5 +1,6 @@ discard """ targets: "cpp" +outputsub: "" """ import nativesockets diff --git a/tests/cpp/tsigbreak.nim b/tests/cpp/tsigbreak.nim index 9a381d84f..14d29adf7 100644 --- a/tests/cpp/tsigbreak.nim +++ b/tests/cpp/tsigbreak.nim @@ -1,5 +1,6 @@ discard """ targets: "cpp" + action: compile """ import tables, lists diff --git a/tests/defaultprocparam/tdefaultprocparam.nim b/tests/defaultprocparam/tdefaultprocparam.nim index 23ecf72e9..5f8c1adab 100644 --- a/tests/defaultprocparam/tdefaultprocparam.nim +++ b/tests/defaultprocparam/tdefaultprocparam.nim @@ -1,4 +1,8 @@ - +discard """ +output: ''' +hi +''' +""" import mdefaultprocparam p() diff --git a/tests/destructor/t6434.nim b/tests/destructor/t6434.nim index 9c912f1f9..c9ad213c2 100644 --- a/tests/destructor/t6434.nim +++ b/tests/destructor/t6434.nim @@ -1,21 +1,26 @@ discard """ exitcode: 0 - output: '''assingment -assingment -assingment -assingment -''' + output: "" """ type Foo* = object boo: int +var sink_counter = 0 +var assign_counter = 0 + +proc `=sink`(dest: var Foo, src: Foo) = + sink_counter.inc + proc `=`(dest: var Foo, src: Foo) = - debugEcho "assingment" + assign_counter.inc proc test(): auto = var a,b : Foo - return (a, b) + return (a, b, Foo(boo: 5)) + +var (a, b, _) = test() -var (a, b) = test() \ No newline at end of file +doAssert: assign_counter == 0 +doAssert: sink_counter == 9 \ No newline at end of file diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim index d20596415..fd8fc34d9 100644 --- a/tests/destructor/tatomicptrs.nim +++ b/tests/destructor/tatomicptrs.nim @@ -8,6 +8,8 @@ allocating deallocating deallocating deallocating +allocating +deallocating ''' cmd: '''nim c --newruntime $file''' """ @@ -23,8 +25,7 @@ template incRef(x) = template decRef(x): untyped = atomicDec(x.refcount) -proc makeShared*[T](x: T): SharedPtr[T] = - # XXX could benefit from 'sink' parameter. +proc makeShared*[T](x: sink T): SharedPtr[T] = # XXX could benefit from a macro that generates it. result = cast[SharedPtr[T]](allocShared(sizeof(x))) result.x[] = x @@ -59,6 +60,9 @@ proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) = echo "deallocating" dest.x = src.x +proc get*[T](s: SharedPtr[T]): lent T = + s.x[] + template `.`*[T](s: SharedPtr[T]; field: untyped): untyped = s.x.field @@ -99,3 +103,47 @@ proc main = main() + + +#------------------------------------------------------- +#bug #9781 + +type + MySeq* [T] = object + refcount: int + len: int + data: ptr UncheckedArray[T] + +proc `=destroy`*[T](m: var MySeq[T]) {.inline.} = + if m.data != nil: + deallocShared(m.data) + m.data = nil + +proc `=`*[T](m: var MySeq[T], m2: MySeq[T]) = + if m.data == m2.data: return + if m.data != nil: + `=destroy`(m) + + m.len = m2.len + let bytes = m.len.int * sizeof(float) + if bytes > 0: + m.data = cast[ptr UncheckedArray[T]](allocShared(bytes)) + copyMem(m.data, m2.data, bytes) + +proc `=sink`*[T](m: var MySeq[T], m2: MySeq[T]) {.inline.} = + if m.data != m2.data: + if m.data != nil: + `=destroy`(m) + m.len = m2.len + m.data = m2.data + +proc len*[T](m: MySeq[T]): int {.inline.} = m.len + +proc newMySeq*[T](size: int, initial_value: T): MySeq[T] = + result.len = size + if size > 0: + result.data = cast[ptr UncheckedArray[T]](allocShared(sizeof(T) * size)) + + +let x = makeShared(newMySeq(10, 1.0)) +doAssert: x.get().len == 10 diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 50aecf46d..26cc682b5 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -60,3 +60,66 @@ for x in getPony(): # XXX this needs to be enabled once top level statements # produce destructor calls again. #echo "Pony is dying!" + + +#------------------------------------------------------------ +#-- Move into tuple constructor and move on tuple unpacking +#------------------------------------------------------------ + +type + MySeqNonCopyable* = object + len: int + data: ptr UncheckedArray[float] + +proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} = + if m.data != nil: + deallocShared(m.data) + m.data = nil + +proc `=`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.} + +proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} = + if m.data != m2.data: + if m.data != nil: + `=destroy`(m) + m.len = m2.len + m.data = m2.data + +proc len*(m: MySeqNonCopyable): int {.inline.} = m.len + +proc `[]`*(m: MySeqNonCopyable; i: int): float {.inline.} = + m.data[i.int] + +proc `[]=`*(m: var MySeqNonCopyable; i, val: float) {.inline.} = + m.data[i.int] = val + +proc setTo(s: var MySeqNonCopyable, val: float) = + for i in 0..<s.len.int: + s.data[i] = val + +proc newMySeq*(size: int, initial_value = 0.0): MySeqNonCopyable =# + result.len = size + if size > 0: + result.data = cast[ptr UncheckedArray[float]](createShared(float, size)) + + result.setTo(initial_value) + +proc myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) = + result = (newMySeq(x, 1.0), newMySeq(y, 5.0)) + +proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] = + var cc = newMySeq(y, 5.0) + (a: newMySeq(x, 1.0), b:0, c: cc) + +let (seq1, seq2) = myfunc(2, 3) +doAssert seq1.len == 2 +doAssert seq1[0] == 1.0 +doAssert seq2.len == 3 +doAssert seq2[0] == 5.0 + +var (seq3, i, _) = myfunc2(2, 3) +doAssert seq3.len == 2 +doAssert seq3[0] == 1.0 + +var seq4, seq5: MySeqNonCopyable +(seq4, i, seq5) = myfunc2(2, 3) \ No newline at end of file diff --git a/tests/dir with space/tspace.nim b/tests/dir with space/tspace.nim index 2b74fa629..59237c9a1 100644 --- a/tests/dir with space/tspace.nim +++ b/tests/dir with space/tspace.nim @@ -1,3 +1,6 @@ -# Test for the compiler to be able to compile a Nim file with spaces in it. +discard """ +output: "Successful" +""" +# Test for the compiler to be able to compile a Nim file with spaces in the directory name. echo("Successful") diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index 99144e324..a3dd966a0 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +1 +1 +''' +""" + # Test the discardable pragma proc p(x, y: int): int {.discardable.} = diff --git a/tests/dll/server.nim b/tests/dll/server.nim index e6b80df88..5ce1976ce 100644 --- a/tests/dll/server.nim +++ b/tests/dll/server.nim @@ -1,4 +1,5 @@ discard """ +action: compile cmd: "nim $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file" """ @@ -25,10 +26,3 @@ proc newOp(k: TNodeKind, a, b: PNode): PNode {.exportc: "newOp", dynlib.} = proc buildTree(x: int): PNode {.exportc: "buildTree", dynlib.} = result = newOp(nkMul, newOp(nkAdd, newLit(x), newLit(x)), newLit(x)) - -when false: - # Test the GC: - for i in 0..100_000: - discard buildTree(2) - - echo "Done" diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim index 3dd83786f..6a4eea155 100644 --- a/tests/effects/teffects6.nim +++ b/tests/effects/teffects6.nim @@ -1,3 +1,8 @@ +discard """ +action: compile +""" + +# XXX: it is not actually tested if the effects are inferred type PMenu = ref object diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim new file mode 100644 index 000000000..929da8843 --- /dev/null +++ b/tests/errmsgs/treportunused.nim @@ -0,0 +1,29 @@ +discard """ + nimout: ''' +treportunused.nim(19, 10) Hint: 'treportunused.s1(a: string)[declared in treportunused.nim(19, 9)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(26, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(22, 6) Hint: 'treportunused.s4()[declared in treportunused.nim(22, 5)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(25, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(24, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(23, 6) Hint: 'treportunused.s5(a: T)[declared in treportunused.nim(23, 5)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(20, 10) Hint: 'treportunused.s2()[declared in treportunused.nim(20, 9)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(29, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(27, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(21, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(28, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +''' +""" + +# bug #9764 + +iterator s1(a:string): int = discard +iterator s2(): int = discard +template s3(): untyped = 123 +proc s4(): int = 123 +proc s5[T](a: T): int = 123 +macro s6(a: int): untyped = discard +const s7 = 0 +let s8 = 0 +var s9: int +type s10 = object +type s11 = type(1.2) diff --git a/tests/errmsgs/tshow_asgn.nim b/tests/errmsgs/tshow_asgn.nim index 1627c9b71..28a9acbeb 100644 --- a/tests/errmsgs/tshow_asgn.nim +++ b/tests/errmsgs/tshow_asgn.nim @@ -1,7 +1,7 @@ discard """ errormsg: "type mismatch: got <int> but expected 'cshort = int16'" line: 12 - column: 10 + column: 27 file: "tshow_asgn.nim" """ diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim new file mode 100644 index 000000000..5d5164f4f --- /dev/null +++ b/tests/exception/t9657.nim @@ -0,0 +1,6 @@ +discard """ + action: run + exitcode: 1 +""" +close stdmsg +writeLine stdmsg, "exception!" diff --git a/tests/exprs/tifexpr_typeinference.nim b/tests/exprs/tifexpr_typeinference.nim index d02492a34..ccaea3e80 100644 --- a/tests/exprs/tifexpr_typeinference.nim +++ b/tests/exprs/tifexpr_typeinference.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + #bug #712 import tables diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim index 45cfbfb84..989ca8bcb 100644 --- a/tests/flags/tgenscript.nim +++ b/tests/flags/tgenscript.nim @@ -1,6 +1,7 @@ discard """ file: "tgenscript.nim" target: "c" + action: compile """ echo "--genscript" diff --git a/tests/generics/t2tables.nim b/tests/generics/t2tables.nim index 3ef5e621e..e4b1fb967 100644 --- a/tests/generics/t2tables.nim +++ b/tests/generics/t2tables.nim @@ -1,3 +1,6 @@ +discard """ +action: compile +""" # bug #3669 @@ -10,4 +13,3 @@ type var g: G[string] echo g.rnodes["foo"] - diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 6897d9de2..34b415446 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +312 +1000000 +1000000 +500000 +0 +''' +""" + import strutils type diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim index 348d3da6e..73c8af825 100644 --- a/tests/generics/tgenericvariant.nim +++ b/tests/generics/tgenericvariant.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +Test +abcxyz123 +''' +""" + +proc fakeReadLine(): string = + "abcxyz123" + type TMaybe[T] = object case empty: bool @@ -12,7 +22,7 @@ proc Nothing[T](): TMaybe[T] = result.empty = true proc safeReadLine(): TMaybe[string] = - var r = stdin.readLine() + var r = fakeReadLine() if r == "": return Nothing[string]() else: return Just(r) @@ -21,3 +31,4 @@ when isMainModule: echo(Test.value) var mSomething = safeReadLine() echo(mSomething.value) + mSomething = safeReadLine() diff --git a/tests/generics/tlateboundstatic.nim b/tests/generics/tlateboundstatic.nim index f68f95f8d..90b44aa8e 100644 --- a/tests/generics/tlateboundstatic.nim +++ b/tests/generics/tlateboundstatic.nim @@ -1,5 +1,5 @@ discard """ - msg: "array[0..3, int]" + nimout: "array[0..3, int]" """ type diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index def1acfe1..f2e9cafa9 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -1,5 +1,6 @@ discard """ cmd: "nim $target --hints:on --threads:on $options $file" + action: compile """ type @@ -36,4 +37,3 @@ when isMainModule: echo("test") joinThread(thr) os.sleep(3000) - diff --git a/tests/global/tglobalforvar.nim b/tests/global/tglobalforvar.nim index af75df5c8..bc18f33f2 100644 --- a/tests/global/tglobalforvar.nim +++ b/tests/global/tglobalforvar.nim @@ -1,7 +1,9 @@ +discard """ +output: 100 +""" var funcs: seq[proc (): int {.nimcall.}] = @[] for i in 0..10: funcs.add((proc (): int = return i * i)) echo(funcs[3]()) - diff --git a/tests/init/t8314.nim b/tests/init/t8314.nim index 59d46eb33..47c8480c2 100644 --- a/tests/init/t8314.nim +++ b/tests/init/t8314.nim @@ -1,8 +1,14 @@ discard """ nimout: ''' -t8314.nim(8, 7) Hint: BEGIN [User] -t8314.nim(19, 7) Hint: END [User] +t8314.nim(14, 7) Hint: BEGIN [User] +t8314.nim(25, 7) Hint: END [User] ''' + +output: ''' +1 +1 +1 +''' """ {.hint: "BEGIN".} diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index 891f1e96c..67c0c4d8b 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,6 +1,7 @@ discard """ - msg: "Warning: 'y' might not have been initialized [Uninit]" + nimout: "Warning: 'y' might not have been initialized [Uninit]" line:34 + action: compile """ import strutils diff --git a/tests/iter/timplicit_auto.nim b/tests/iter/timplicit_auto.nim index d5cb95eb8..1b9f06843 100644 --- a/tests/iter/timplicit_auto.nim +++ b/tests/iter/timplicit_auto.nim @@ -15,4 +15,4 @@ iterator fields(a = (0,0), b = (h-1,w-1)): auto = yield (y,x) for y,x in fields(): - stdout.write disp[univ(x, y)] + doAssert disp[univ(x, y)] == disp[Tree] diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index c4143ae4f..22be1bad5 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -1,3 +1,16 @@ +discard """ +output: ''' +testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest2!test3?hi +what's +your +name +hi +what's +your +name +''' +""" + # Test the new iterators iterator xrange(fromm, to: int, step = 1): int = diff --git a/tests/iter/titer_no_tuple_unpack.nim b/tests/iter/titer_no_tuple_unpack.nim index 13ec11bd6..d8df10189 100644 --- a/tests/iter/titer_no_tuple_unpack.nim +++ b/tests/iter/titer_no_tuple_unpack.nim @@ -1,3 +1,18 @@ +discard """ +output: ''' +3 4 +4 5 +5 6 +6 7 +7 8 +(x: 3, y: 4) +(x: 4, y: 5) +(x: 5, y: 6) +(x: 6, y: 7) +(x: 7, y: 8) +''' +""" + iterator xrange(fromm, to: int, step = 1): tuple[x, y: int] = var a = fromm @@ -10,4 +25,3 @@ for a, b in xrange(3, 7): for tup in xrange(3, 7): echo tup - diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim index 1e70ce247..9eea085e3 100644 --- a/tests/iter/titervaropenarray.nim +++ b/tests/iter/titervaropenarray.nim @@ -11,6 +11,3 @@ iterator iterAndZero(a: var openArray[int]): int = var x = [[1, 2, 3], [4, 5, 6]] for y in iterAndZero(x[0]): write(stdout, $y) #OUT 123 - - - diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim index 5149eb9c2..c5067ba31 100644 --- a/tests/iter/tpermutations.nim +++ b/tests/iter/tpermutations.nim @@ -1,3 +1,14 @@ +discard """ +output: ''' +@[@[1.0, 2.0], @[3.0, 4.0]] +perm: 10.0 det: -2.0 +@[@[1.0, 2.0, 3.0, 4.0], @[4.0, 5.0, 6.0, 7.0], @[7.0, 8.0, 9.0, 10.0], @[10.0, 11.0, 12.0, 13.0]] +perm: 29556.0 det: 0.0 +@[@[0.0, 1.0, 2.0, 3.0, 4.0], @[5.0, 6.0, 7.0, 8.0, 9.0], @[10.0, 11.0, 12.0, 13.0, 14.0], @[15.0, 16.0, 17.0, 18.0, 19.0], @[20.0, 21.0, 22.0, 23.0, 24.0]] +perm: 6778800.0 det: 0.0 +''' +""" + import sequtils, sugar diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim index 2f024ee7e..279e7d950 100644 --- a/tests/iter/tshallowcopy_closures.nim +++ b/tests/iter/tshallowcopy_closures.nim @@ -1,5 +1,9 @@ discard """ ccodecheck: "!@('{' \\s* 'NI HEX3Astate;' \\s* '}')" + output: ''' +a1 10 +a1 9 +''' """ # bug #1803 @@ -26,6 +30,6 @@ var z: TaskFn discard x() -z = x #shallowCopy(z, x) -z = y #shallowCopy(z, y) +shallowCopy(z, x) +shallowCopy(z, y) discard x() diff --git a/tests/iter/twrap_walkdir.nim b/tests/iter/twrap_walkdir.nim index 4ac487d8e..1d52e9791 100644 --- a/tests/iter/twrap_walkdir.nim +++ b/tests/iter/twrap_walkdir.nim @@ -1,5 +1,6 @@ - - +discard """ +action: compile +""" import os diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index ee2790e54..32eef494e 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -403,7 +403,7 @@ block: # yield in blockexpr test(it, 1, 2, 3) -block: #8851 +block: #8851 type Foo = ref object of RootObj template someFoo(): Foo = @@ -454,5 +454,18 @@ block: #9694 - yield in ObjConstr test(it, 1, 2) +block: #9716 + iterator it(): int {.closure.} = + var a = 0 + for i in 1 .. 3: + var a: int # Make sure the "local" var is reset + var b: string # ditto + yield 1 + a += 5 + b &= "hello" + doAssert(a == 5) + doAssert(b == "hello") + test(it, 1, 1, 1) + echo "ok" diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim new file mode 100644 index 000000000..9aca6d45b --- /dev/null +++ b/tests/js/t9410.nim @@ -0,0 +1,454 @@ +template doAssert(exp: untyped) = + when defined(echot9410): + let r = exp + echo $(instantiationInfo().line) & ":\n " & astToStr(exp) & "\n was " & repr(r) + when not defined(noassertt9410): + system.doAssert r + else: + when not defined(noassertt9410): + system.doAssert exp + +template tests = + block: + var i = 0 + i = 2 + + var y: ptr int + doAssert y == nil + doAssert isNil(y) + y = i.addr + y[] = 3 + doAssert i == 3 + doAssert i == y[] + + let z = i.addr + z[] = 4 + doAssert i == 4 + doAssert i == y[] and y[] == z[] + + var hmm = (a: (b: z)) + var hmmptr = hmm.a.b.addr + hmmptr[][] = 5 + + doAssert i == 5 + doAssert y == z + doAssert z == hmmptr[] + doAssert 5 == y[] and 5 == z[] and 5 == hmmptr[][] + + block: + var someint = 500 + + let p: ptr int = someint.addr + let tup = (f: p) + let tcopy = tup + var vtcopy = tcopy + p[] = 654 + doAssert p[] == 654 + doAssert tup.f[] == 654 + doAssert tcopy.f[] == 654 + doAssert vtcopy.f[] == 654 + + block: + var someint = 500 + + var p: ptr int = someint.addr + let arr = [p] + let arrc = arr + p[] = 256 + doAssert someint == 256 + doAssert p[] == 256 + doAssert arr[0][] == 256 + doAssert arrc[0][] == 256 + + block: + var someref: ref int + new(someref) + var someref2 = someref + + var tup1 = (f: someref) + tup1.f = someref + let tup2 = tup1 + + someref[] = 543 + + proc passref(r: var ref int): var ref int = r + new(passref(someref)) + + doAssert someref[] == 0 + doAssert tup1.f[] == 543 + doAssert tup2.f[] == 543 + doAssert someref2[] == 543 + + block: + type Whatever = object + i: ref int + + var someref: ref int + new(someref) + someref[] = 10 + + let w = Whatever(i: someref) + var wcopy = w + + someref[] = 20 + + doAssert w.i[] == 20 + doAssert someref[] == 20 + doAssert wcopy.i[] == 20 + doAssert w.i == wcopy.i + #echo w.i[], " ", someref[], " ", wcopy.i[] + + block: + var oneseq: ref seq[ref int] + new(oneseq) + var aref: ref int + new(aref) + aref[] = 123 + let arefs = [aref] + oneseq[] &= arefs[0] + oneseq[] &= aref + aref[] = 222 + new(aref) + doAssert oneseq[0] == oneseq[1] + doAssert oneseq[0][] == 222 + doAssert oneseq[1][] == 222 + doAssert aref[] == 0 + + block: + var seqs: ref seq[ref seq[ref int]] + new(seqs) + seqs[] = newSeq[ref seq[ref int]](1) + new(seqs[0]) + seqs[0][] = newSeq[ref int](0) + + var aref: ref int + new aref + aref[] = 654 + + let arefs = [aref] + doAssert arefs[0] == aref + seqs[0][] &= arefs[0] + seqs[0][] &= aref + seqs[0][1][] = 456 + let seqs2 = seqs + let same = seqs2[0][0] == seqs2[0][1] + doAssert arefs[0] == aref + doAssert aref[] == 456 + doAssert seqs[].len == 1 + doAssert seqs[0][].len == 2 + doAssert seqs[0][0][] == 456 + doAssert seqs[0][1][] == 456 + doAssert same + + block: + type Obj = object + x, y: int + + var objrefs: seq[ref Obj] = @[(ref Obj)(nil), nil, nil] + objrefs[2].new + objrefs[2][] = Obj(x: 123, y: 321) + objrefs[1] = objrefs[2] + doAssert objrefs[0] == nil + doAssert objrefs[1].y == 321 + doAssert objrefs[2].y == 321 + doAssert objrefs[1] == objrefs[2] + + block: + var refs: seq[ref string] = @[(ref string)(nil), nil, nil] + refs[1].new + refs[1][] = "it's a ref!" + refs[0] = refs[1] + refs[2] = refs[1] + new(refs[0]) + doAssert refs[0][] == "" + doAssert refs[1][] == "it's a ref!" + doAssert refs[2][] == "it's a ref!" + doAssert refs[1] == refs[2] + + block: + var retaddr_calls = 0 + proc retaddr(p: var int): var int = + retaddr_calls += 1 + p + + var tfoo_calls = 0 + proc tfoo(x: var int) = + tfoo_calls += 1 + x += 10 + var y = x.addr + y[] += 20 + retaddr(x) += 30 + let z = retaddr(x).addr + z[] += 40 + + var ints = @[1, 2, 3] + tfoo(ints[1]) + doAssert retaddr_calls == 2 + doAssert tfoo_calls == 1 + doAssert ints[1] == 102 + + var tbar_calls = 0 + proc tbar(x: var int): var int = + tbar_calls += 1 + x + + tbar(ints[2]) += 10 + tbar(ints[2]) *= 2 + doAssert tbar_calls == 2 + + var tqux_calls = 0 + proc tqux(x: var int): ptr int = + tqux_calls += 1 + x.addr + + discard tqux(ints[2]) == tqux(ints[2]) + doAssert tqux_calls == 2 + doAssert isNil(tqux(ints[2])) == false + doAssert tqux_calls == 3 + + var tseq_calls = 0 + proc tseq(x: var seq[int]): var seq[int] = + tseq_calls += 1 + x + + tseq(ints) &= 999 + doAssert tseq_calls == 1 + doAssert ints == @[1, 102, 26, 999] + + var rawints = @[555] + rawints &= 666 + doAssert rawints == @[555, 666] + + var resetints_calls = 0 + proc resetInts(): int = + resetints_calls += 1 + ints = @[0, 0, 0] + 1 + + proc incr(x: var int; b: int): var int = + x = x + b + x + + var q = 0 + var qp = q.addr + qp[] += 123 + doAssert q == 123 + # check order of evaluation + doAssert (resetInts() + incr(q, tqux(ints[2])[])) == 124 + + block: # reset + var calls = 0 + proc passsomething(x: var int): var int = + calls += 1 + x + + var + a = 123 + b = 500 + c = a.addr + reset(passsomething(a)) + doAssert calls == 1 + reset(b) + doAssert a == b + reset(c) + doAssert c == nil + + block: # strings + var calls = 0 + proc stringtest(s: var string): var string = + calls += 1 + s + + var somestr: string + + stringtest(somestr) &= 'a' + stringtest(somestr) &= 'b' + doAssert calls == 2 + doAssert somestr == "ab" + stringtest(somestr) &= "woot!" + doAssert somestr == "abwoot!" + doAssert calls == 3 + + doAssert stringtest(somestr).len == 7 + doAssert calls == 4 + doAssert high(stringtest(somestr)) == 6 + doAssert calls == 5 + + var somestr2: string + stringtest(somestr2).setLen(stringtest(somestr).len) + doAssert calls == 7 + doAssert somestr2.len == somestr.len + + var somestr3: string + doAssert (somestr3 & "foo") == "foo" + + block: + var a, b, c, d: string + d = a & b & c + doAssert d == "" + d = stringtest(a) & stringtest(b) & stringtest(c) + doAssert calls == 10 + doAssert d == "" + + block: # seqs + var calls = 0 + proc seqtest(s: var seq[int]): var seq[int] = + calls += 1 + s + + var someseq: seq[int] + + seqtest(someseq) &= 1 + seqtest(someseq) &= 2 + doAssert calls == 2 + doAssert someseq == @[1, 2] + seqtest(someseq) &= @[3, 4, 5] + doAssert someseq == @[1, 2, 3, 4, 5] + doAssert calls == 3 + + doAssert seqtest(someseq).len == 5 + doAssert calls == 4 + doAssert high(seqtest(someseq)) == 4 + doAssert calls == 5 + + # genArrayAddr + doAssert seqtest(someseq)[2] == 3 + doAssert calls == 6 + + seqtest(someseq).setLen(seqtest(someseq).len) + doAssert calls == 8 + + var somenilseq: seq[int] + seqtest(somenilseq).setLen(3) + doAssert calls == 9 + doAssert somenilseq[1] == 0 + + someseq = @[1, 2, 3] + doAssert (seqtest(someseq) & seqtest(someseq)) == @[1, 2, 3, 1, 2, 3] + + + block: # mInc, mDec + var calls = 0 + proc someint(x: var int): var int = + calls += 1 + x + + var x = 10 + + inc(someint(x)) + doAssert x == 11 + doAssert calls == 1 + + dec(someint(x)) + doAssert x == 10 + doAssert calls == 2 + + block: # uints + var calls = 0 + proc passuint(x: var uint32): var uint32 = + calls += 1 + x + + var u: uint32 = 5 + passuint(u) += 1 + doAssert u == 6 + doAssert calls == 1 + + passuint(u) -= 1 + doAssert u == 5 + doAssert calls == 2 + + passuint(u) *= 2 + doAssert u == 10 + doAssert calls == 3 + + block: # objs + type Thing = ref object + x, y: int + + var a, b: Thing + a = Thing() + b = a + + doAssert a == b + + var calls = 0 + proc passobj(o: var Thing): var Thing = + calls += 1 + o + + passobj(b) = Thing(x: 123) + doAssert calls == 1 + doAssert a != b + doAssert b.x == 123 + + var passobjptr_calls = 0 + proc passobjptr(o: var Thing): ptr Thing = + passobjptr_calls += 1 + o.addr + + passobjptr(b)[] = Thing(x: 234) + doAssert passobjptr_calls == 1 + doAssert a != b + doAssert b.x == 234 + passobjptr(b)[].x = 500 + doAssert b.x == 500 + + var pptr = passobjptr(b) + pptr.x += 100 + doAssert b.x == 600 + + proc getuninitptr(): ptr int = + return + + doAssert getuninitptr() == nil + + block: # pointer casting + var obj = (x: 321, y: 543) + var x = 500 + + var objptr = obj.addr + var xptr = x.addr + + var p1, p2: pointer + p1 = cast[pointer](objptr) + p2 = cast[pointer](xptr) + doAssert p1 != p2 + + p1 = cast[pointer](objptr) + p2 = cast[pointer](objptr) + doAssert p1 == p2 + + let objptr2 = cast[type(objptr)](p2) + doAssert objptr == objptr2 + + p1 = cast[pointer](xptr) + p2 = cast[pointer](xptr) + doAssert p1 == p2 + + let xptr2 = cast[type(xptr)](p2) + doAssert xptr == xptr2 + + when false: + block: # openarray + # Error: internal error: genAddr: nkStmtListExpr + var calls = 0 + proc getvarint(x: var openarray[int]): var int = + calls += 1 + if true: + x[1] + else: + x[0] + + var arr = [1, 2, 3] + getvarint(arr) += 5 + doAssert calls == 1 + doAssert arr[1] == 7 + +proc tests_in_proc = + tests + +# since pointers are handled differently in global/local contexts +# let's just run all of them twice +tests_in_proc() +tests diff --git a/tests/js/tcopying.nim b/tests/js/tcopying.nim index 387df9cd3..c58a080e9 100644 --- a/tests/js/tcopying.nim +++ b/tests/js/tcopying.nim @@ -2,6 +2,10 @@ discard """ output: '''123 2 9 2 9 +1 124 +true false +100 300 100 +1 ''' """ @@ -35,3 +39,34 @@ block: obj.ary2[1] = 9 echo ary1[1], " ", obj.ary2[1] + +block: + type TestObj = object + x, y: int + + let obj = TestObj(x: 1, y: 2) + var s = @[obj] + s[0].x += 123 + echo obj.x, " ", s[0].x + +block: + var nums = {1, 2, 3, 4} + let obj = (n: nums) + nums.incl 5 + echo (5 in nums), " ", (5 in obj.n) + +block: + let tup1 = (a: 100) + var tup2 = (t: (t2: tup1)) + var tup3 = tup1 + tup2.t.t2.a = 300 + echo tup1.a, " ", tup2.t.t2.a, " ", tup3.a + +block: + proc foo(arr: array[2, int]) = + var s = @arr + s[0] = 500 + + var nums = [1, 2] + foo(nums) + echo nums[0] \ No newline at end of file diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 213d05964..2420c60f6 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -154,7 +154,7 @@ block: # Test JsAssoc .= and . block: proc test(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 11 obj.`$!&` = 42 @@ -168,7 +168,7 @@ block: # Test JsAssoc .() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e obj.a(10) == 100 echo test() @@ -176,7 +176,7 @@ block: # Test JsAssoc []() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e let call = obj["a"] call(10) == 100 @@ -185,7 +185,7 @@ block: # Test JsAssoc Iterators block: proc testPairs(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -202,7 +202,7 @@ block: return false working proc testItems(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -211,13 +211,13 @@ block: working = working and v in [10, 20, 30] working proc testKeys(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 obj.c = 30 for v in obj.keys: - working = working and v in ["a", "b", "c"] + working = working and v in [cstring"a", cstring"b", cstring"c"] working proc test(): bool = testPairs() and testItems() and testKeys() echo test() @@ -226,8 +226,8 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = newJsAssoc[string, int]() + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = newJsAssoc[cstring, int]() obj.a = 22 obj.b = 55 obj.a == comparison.a and obj.b == comparison.b @@ -237,15 +237,15 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = JsAssoc[string, int]{ a: 22, b: 55 } + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } var working = true working = working and compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) working = working and comparison.a == obj.a and comparison.b == obj.b working = working and - not compiles(JsAssoc[string, int]{ a: "test" }) + not compiles(JsAssoc[cstring, int]{ a: "test" }) working echo test() diff --git a/tests/lexer/tident.nim b/tests/lexer/tident.nim index 3327344a5..e5177436d 100644 --- a/tests/lexer/tident.nim +++ b/tests/lexer/tident.nim @@ -1,3 +1,16 @@ +discard """ +output: ''' +Length correct +Correct +Correct +Correct +Correct +Correct +Correct +Correct +Correct +''' +""" type TIdObj* = object of RootObj @@ -19,4 +32,3 @@ proc myNewString(L: int): string {.inline.} = echo("Wrong") var s = myNewString(8) - diff --git a/tests/lookups/test.nim b/tests/lookups/test.nim index a17d235a4..56f39a4d7 100644 --- a/tests/lookups/test.nim +++ b/tests/lookups/test.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +[Suite] memoization + +''' +""" + # This file needs to be called 'test' nim to provoke a clash # with the unittest.test name. Issue # @@ -14,4 +21,3 @@ proc fib(n: int): int = 40 suite "memoization": test "recursive function memoization": check fastFib(40) == fib(40) - diff --git a/tests/lookups/tprefer_proc.nim b/tests/lookups/tprefer_proc.nim deleted file mode 100644 index 57ee8e539..000000000 --- a/tests/lookups/tprefer_proc.nim +++ /dev/null @@ -1,4 +0,0 @@ - -# bug #4353 -import random -echo random[int](low(int) .. high(int)) diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim index 2abcd98ce..a493d6a88 100644 --- a/tests/macros/tbindsym.nim +++ b/tests/macros/tbindsym.nim @@ -1,5 +1,5 @@ discard """ - msg: '''initApple + nimout: '''initApple deinitApple Coral enum diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim index 0a1836886..0e0581f6a 100644 --- a/tests/macros/tdumpastgen.nim +++ b/tests/macros/tdumpastgen.nim @@ -1,5 +1,5 @@ discard """ -msg: '''nnkStmtList.newTree( +nimout: '''nnkStmtList.newTree( nnkVarSection.newTree( nnkIdentDefs.newTree( newIdentNode("x"), diff --git a/tests/macros/tdumptree.nim b/tests/macros/tdumptree.nim index 58b011b45..f540306c4 100644 --- a/tests/macros/tdumptree.nim +++ b/tests/macros/tdumptree.nim @@ -1,13 +1,14 @@ discard """ -msg: '''StmtList +nimout: ''' +StmtList VarSection IdentDefs - Ident !"x" + Ident "x" Empty Call DotExpr - Ident !"foo" - Ident !"create" + Ident "foo" + Ident "create" IntLit 56''' """ diff --git a/tests/macros/tescape_var_into_quotedo_as_const.nim b/tests/macros/tescape_var_into_quotedo_as_const.nim new file mode 100644 index 000000000..1ed93f012 --- /dev/null +++ b/tests/macros/tescape_var_into_quotedo_as_const.nim @@ -0,0 +1,36 @@ +discard """ + output: '''ok''' +""" +# bug #9864 +import macros, tables + +proc bar(shOpt: Table[string, int]) = discard + +macro dispatchGen(): untyped = + var shOpt = initTable[string, int]() + shOpt["foo"] = 10 + result = quote do: + bar(`shOpt`) + +dispatchGen() + +type + Foo = object + data: seq[int] + +proc barB(a: Foo) = discard + +proc shOptB(): auto = + var shOpt: Foo + shOpt.data.setLen 1 # fails + shOpt + +macro dispatchGenB(): untyped = + var shOpt = shOptB() # fails + + result = quote do: + barB(`shOpt`) + +dispatchGenB() + +echo "ok" diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index a546271ff..d231a4336 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,5 +1,5 @@ discard """ - msg: '''"muhaha" + nimout: '''"muhaha" proc poo(x, y: int) = let y = x echo ["poo"]''' @@ -46,3 +46,22 @@ static: doAssert isSameOwner(foo, poo) doAssert isSameOwner(foo, echo) == false doAssert isSameOwner(poo, len) == false + +#--------------------------------------------------------------- + +macro check_gen_proc(ex: typed): (bool, bool) = + let lenChoice = bindsym"len" + var is_equal = false + var is_instance_of = false + for child in lenChoice: + if not is_equal: + is_equal = ex[0] == child + if not is_instance_of: + is_instance_of = isInstantiationOf(ex[0], child) + + result = nnkTupleConstr.newTree(newLit(is_equal), newLit(is_instance_of)) + +# check that len(seq[int]) is not equal to bindSym"len", but is instance of it +let a = @[1,2,3] +assert: check_gen_proc(len(a)) == (false, true) + diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index fa02bce57..77a55471f 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -1,6 +1,8 @@ discard """ -msg: '''ObjectTy(Sym(Model), RecList(Sym(name), Sym(password))) -BracketExpr(Sym(typeDesc), Sym(User))''' +output: ''' +(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password"))) +(BracketExpr (Sym "typeDesc") (Sym "User")) +''' """ import strutils, macros diff --git a/tests/macros/tgettype2.nim b/tests/macros/tgettype2.nim index f129e6e1b..c579cf6ff 100644 --- a/tests/macros/tgettype2.nim +++ b/tests/macros/tgettype2.nim @@ -1,3 +1,35 @@ +discard """ +output: ''' +############ +#### gt #### +############ +gt(Foo): typeDesc[Foo] +gt(Bar): typeDesc[Bar] +gt(Baz): typeDesc[int] +gt(foo): distinct[int] +gt(bar): distinct[int] +gt(baz): int, int +gt(v): seq[int] +gt(vv): seq[float] +gt(t): distinct[tuple[int, int]] +gt(tt): distinct[tuple[float, float]] +gt(s): distinct[tuple[int, int]] +############# +#### gt2 #### +############# +gt2(Foo): Foo +gt2(Bar): Bar +gt2(Baz): Baz +gt2(foo): Foo +gt2(bar): Bar +gt2(baz): Baz +gt2(v): seq[int] +gt2(vv): seq[float] +gt2(t): MyType[system.int] +gt2(tt): MyType[system.float] +gt2(s): MySimpleType +''' +""" import macros, typetraits diff --git a/tests/macros/tmacrogenerics.nim b/tests/macros/tmacrogenerics.nim index 919a15b46..e4acdc321 100644 --- a/tests/macros/tmacrogenerics.nim +++ b/tests/macros/tmacrogenerics.nim @@ -1,8 +1,10 @@ discard """ file: "tmacrogenerics.nim" - msg: ''' -instantiation 1 with typedesc and typedesc -counter: 1 + nimout: ''' +instantiation 1 with None and None +instantiation 2 with None and None +instantiation 3 with None and None +counter: 3 ''' output: "int\nfloat\nint\nstring" """ diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index ecdcd5da9..657f30fc4 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -1,13 +1,10 @@ discard """ - msg: ''' -proc init(foo129050: int; bar129052: typedesc[int]): int = - foo129050 - + nimout: ''' IntLit 5 proc (x: int): string => typeDesc[proc[string, int]] proc (x: int): void => typeDesc[proc[void, int]] proc (x: int) => typeDesc[proc[void, int]] -x => uncheckedArray[int] +x => UncheckedArray[int] a s d @@ -43,8 +40,8 @@ block t7723: proc init(foo: int, bar: typedesc[int]): int = foo - expandMacros: - foo1() + #expandMacros: + foo1() doAssert init(1, int) == 1 @@ -144,11 +141,11 @@ block t1140: result.parse_template body[1].strVal - proc actual: string = tmpli html""" + proc actual: string {.used.} = tmpli html""" <p>Test!</p> """ - proc another: string = tmpli html""" + proc another: string {.used.} = tmpli html""" <p>what</p> """ diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 15bd28a37..9eece00bd 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -1,9 +1,14 @@ discard """ - msg: ''' -range[0 .. 100] -array[0 .. 100, int] -10 -test + nimout: ''' +Infix + Ident "=>" + Call + Ident "name" + Ident "a" + ExprColonExpr + Ident "b" + Ident "cint" + NilLit ''' output: ''' diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim index 3a3b8734b..06ab8a1e2 100644 --- a/tests/macros/tmemit.nim +++ b/tests/macros/tmemit.nim @@ -1,7 +1,9 @@ discard """ - output: '''HELLO WORLD + output: ''' +HELLO WORLD c_func -12''' +12 +''' """ import macros, strutils diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim index b663b81ba..cd1f69116 100644 --- a/tests/macros/tquotedo.nim +++ b/tests/macros/tquotedo.nim @@ -1,3 +1,11 @@ +discard """ +output: ''' +123 +Hallo Welt +Hallo Welt +''' +""" + import macros macro mac(): untyped = diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index ea59936e0..8bd653920 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -1,5 +1,6 @@ discard """ - msg: '''letters + nimout: ''' +letters aa bb numbers @@ -8,7 +9,7 @@ numbers AST a [(11, 22), (33, 44)] AST b -(e: [55, 66], f: [77, 88]) +([55, 66], [77, 88]) 55 10 20Test @@ -44,10 +45,10 @@ const b : Tb = (@[55,66], @[77, 88]) macro mA(data: static[Ta]): untyped = - echo "AST a \n", repr(data) + echo "AST a\n", repr(data) macro mB(data: static[Tb]): untyped = - echo "AST b \n", repr(data) + echo "AST b\n", repr(data) echo data.e[0] mA(a) diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim index 039acfbe9..cfa80e581 100644 --- a/tests/metatype/tbindtypedesc.nim +++ b/tests/metatype/tbindtypedesc.nim @@ -1,10 +1,5 @@ discard """ - msg: '''int int -float float -int int -TFoo TFoo -int float -TFoo TFoo''' + output: '''ok''' """ import typetraits @@ -86,3 +81,4 @@ reject bindArg(int, int, 10, 20, 30, "test") reject bindArg(int, string, 10.0, 20, "test", "nest") reject bindArg(int, string, "test", "nest", 10, 20) +echo "ok" diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim index 5c5380c9f..c5040f9ba 100644 --- a/tests/metatype/tmetatype_issues.nim +++ b/tests/metatype/tmetatype_issues.nim @@ -1,4 +1,15 @@ - +discard """ +output:''' +void +(Field0: "string", Field1: "string") +1 mod 7 +@[2, 2, 2, 2, 2] +impl 2 called +asd +Foo +Bar +''' +""" import typetraits, macros @@ -145,6 +156,3 @@ block t3338: var t2 = Bar[int32]() t2.add() doAssert t2.x == 5 - - - diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim index 3f36abde9..56b9a6218 100644 --- a/tests/metatype/tsemistatic.nim +++ b/tests/metatype/tsemistatic.nim @@ -1,5 +1,5 @@ discard """ - msg: "static 10\ndynamic\nstatic 20\n" + nimout: "static 10\ndynamic\nstatic 20\n" output: "s\nd\nd\ns" """ diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index 02021185f..16a6e56b8 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -1,5 +1,5 @@ discard """ - msg: '''letters + nimout: '''letters aa bb numbers @@ -8,7 +8,7 @@ numbers AST a [(11, 22), (33, 44)] AST b -(e: [55, 66], f: [77, 88]) +([55, 66], [77, 88]) 55 10 20Test @@ -44,10 +44,10 @@ const b : Tb = (@[55,66], @[77, 88]) macro mA(data: static[Ta]): untyped = - echo "AST a \n", repr(data) + echo "AST a\n", repr(data) macro mB(data: static[Tb]): untyped = - echo "AST b \n", repr(data) + echo "AST b\n", repr(data) echo data.e[0] mA(a) diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 3d1cf2ec9..bd973eed1 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +proc Base +proc Child +method Base +yield Base +yield Child +''' +""" + import typetraits type diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 106257828..2765a4231 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -1,5 +1,5 @@ discard """ - msg: "int\nstring\nTBar[int]" + nimout: "int\nstring\nTBar[int]" output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring" disabled: true """ diff --git a/tests/misc/tcmdline.nim b/tests/misc/tcmdline.nim index cb8cb402c..2c4768716 100644 --- a/tests/misc/tcmdline.nim +++ b/tests/misc/tcmdline.nim @@ -1,3 +1,6 @@ +discard """ +outputsub: "Number of parameters: 0" +""" # Test the command line import diff --git a/tests/misc/tcolonisproc.nim b/tests/misc/tcolonisproc.nim index 665e9e604..c10dabcf1 100644 --- a/tests/misc/tcolonisproc.nim +++ b/tests/misc/tcolonisproc.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +1 +2 +''' +""" proc p(a, b: int, c: proc ()) = c() diff --git a/tests/misc/tdllvar.nim b/tests/misc/tdllvar.nim index 1c1238e8d..68029ddf4 100644 --- a/tests/misc/tdllvar.nim +++ b/tests/misc/tdllvar.nim @@ -1,3 +1,7 @@ +discard """ +disabled: true +""" + import os proc getDllName: string = @@ -12,5 +16,3 @@ proc myImport2(s: int) {.cdecl, importc, dynlib: getDllName().} myImport("test2") myImport2(12) - - diff --git a/tests/misc/tendian.nim b/tests/misc/tendian.nim deleted file mode 100644 index 91044f4d5..000000000 --- a/tests/misc/tendian.nim +++ /dev/null @@ -1,3 +0,0 @@ -# test the new endian magic - -writeLine(stdout, repr(system.cpuEndian)) diff --git a/tests/misc/tgetstartmilsecs.nim b/tests/misc/tgetstartmilsecs.nim deleted file mode 100644 index bf508dd54..000000000 --- a/tests/misc/tgetstartmilsecs.nim +++ /dev/null @@ -1,7 +0,0 @@ -# -import times, os - -var start = epochTime() -os.sleep(1000) - -echo epochTime() - start #OUT 1000 diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim index 17e6089ed..6f9d49121 100644 --- a/tests/misc/thallo.nim +++ b/tests/misc/thallo.nim @@ -1,4 +1,8 @@ -# Hallo +discard """ +action: compile +""" + +# noted this seems to be an old test file designed for manual testing. import os, strutils, macros diff --git a/tests/misc/theaproots.nim b/tests/misc/theaproots.nim index 77d0207b0..1ea3c86b9 100644 --- a/tests/misc/theaproots.nim +++ b/tests/misc/theaproots.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + type Bar = object x: int diff --git a/tests/misc/tlastmod.nim b/tests/misc/tlastmod.nim index c622ab518..1cc1d4bd9 100644 --- a/tests/misc/tlastmod.nim +++ b/tests/misc/tlastmod.nim @@ -1,18 +1,25 @@ +discard """ +outputsub: "is newer than" +""" # test the new LastModificationTime() proc +let + file1 = "tests/testdata/data.csv" + file2 = "tests/testdata/doc1.xml" + import os, times, strutils proc main() = var a, b: Time - a = getLastModificationTime(paramStr(1)) - b = getLastModificationTime(paramStr(2)) + a = getLastModificationTime(file1) + b = getLastModificationTime(file2) writeLine(stdout, $a) writeLine(stdout, $b) if a < b: - write(stdout, "$2 is newer than $1\n" % [paramStr(1), paramStr(2)]) + write(stdout, "$2 is newer than $1\n" % [file1, file2]) else: - write(stdout, "$1 is newer than $2\n" % [paramStr(1), paramStr(2)]) + write(stdout, "$1 is newer than $2\n" % [file1, file2]) main() diff --git a/tests/misc/tloops.nim b/tests/misc/tloops.nim index b160500af..61e0baf10 100644 --- a/tests/misc/tloops.nim +++ b/tests/misc/tloops.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +Hello!(x: 1, y: 2, z: 3) +(x: 1.0, y: 2.0) +''' +""" + # Test nested loops and some other things proc andTest() = @@ -84,4 +91,3 @@ proc main[T]() = echo myType2 main[int]() - diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim deleted file mode 100644 index 504628313..000000000 --- a/tests/misc/tmandelbrot.nim +++ /dev/null @@ -1,57 +0,0 @@ -discard """ - cmd: "nim $target --hints:on -d:release $options $file" -""" - -# -*- nim -*- - -import math -import os -import strutils - -type TComplex = tuple[re, im: float] - -proc `+` (a, b: TComplex): TComplex = - return (a.re + b.re, a.im + b.im) - -proc `*` (a, b: TComplex): TComplex = - result.re = a.re * b.re - a.im * b.im - result.im = a.re * b.im + a.im * b.re - -proc abs2 (a: TComplex): float = - return a.re * a.re + a.im * a.im - -var size = parseInt(paramStr(1)) -var bit = 128 -var byteAcc = 0 - -stdout.writeLine("P4") -stdout.write($size) -stdout.write(" ") -stdout.writeLine($size) - -var fsize = float(size) -for y in 0 .. size-1: - var fy = 2.0 * float(y) / fsize - 1.0 - for x in 0 .. size-1: - var z = (0.0, 0.0) - var c = (float(2*x) / fsize - 1.5, fy) - - block iter: - for i in 0 .. 49: - z = z*z + c - if abs2(z) >= 4.0: - break iter - byteAcc = byteAcc + bit - - if bit > 1: - bit = bit div 2 - else: - stdout.write(chr(byteAcc)) - bit = 128 - byteAcc = 0 - - if bit != 128: - stdout.write(chr(byteAcc)) - bit = 128 - byteAcc = 0 - diff --git a/tests/misc/tmemoization.nim b/tests/misc/tmemoization.nim index 840eb3b0d..c65692608 100644 --- a/tests/misc/tmemoization.nim +++ b/tests/misc/tmemoization.nim @@ -1,5 +1,5 @@ discard """ - msg: "test 1\ntest 2\ntest 3" + nimout: "test 1\ntest 2\ntest 3" output: "TEST 1\nTEST 2\nTEST 3" """ diff --git a/tests/misc/tnew.nim b/tests/misc/tnew.nim index 89f34a621..02282dd4a 100644 --- a/tests/misc/tnew.nim +++ b/tests/misc/tnew.nim @@ -1,3 +1,10 @@ +discard """ +outputsub: ''' +Simple tree node allocation worked! +Simple cycle allocation worked! +''' +""" + # Test the implementation of the new operator # and the code generation for gc walkers # (and the garbage collector): diff --git a/tests/misc/tnewuns.nim b/tests/misc/tnewuns.nim deleted file mode 100644 index d6bae4fb1..000000000 --- a/tests/misc/tnewuns.nim +++ /dev/null @@ -1,12 +0,0 @@ -# test the new unsigned operations: - -import - strutils - -var - x, y: int - -x = 1 -y = high(int) - -writeLine(stdout, $ ( x +% y ) ) diff --git a/tests/misc/tprep.nim b/tests/misc/tprep.nim index 8f40300d6..45f25b790 100644 --- a/tests/misc/tprep.nim +++ b/tests/misc/tprep.nim @@ -1,3 +1,11 @@ +discard """ +nimout: ''' +tprep.nim(25, 9) Hint: Case 2 [User] +tprep.nim(27, 11) Hint: Case 2.3 [User] +''' +outputsub: "" +""" + # Test the features that used to belong to the preprocessor import diff --git a/tests/misc/tquicksort.nim b/tests/misc/tquicksort.nim index 0867a3769..017c73fbc 100644 --- a/tests/misc/tquicksort.nim +++ b/tests/misc/tquicksort.nim @@ -17,10 +17,7 @@ proc echoSeq(a: seq[int]) = for i in low(a)..high(a): echo(a[i]) -var - list: seq[int] - -list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3]) -echoSeq(list) - +let list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3]) +let expected = @[1, 2, 3, 4, 6, 7, 9, 12, 15, 23, 56, 89, 123, 356] +doAssert list == expected diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index 07674af18..5009dfcfb 100644 --- a/tests/misc/tradix.nim +++ b/tests/misc/tradix.nim @@ -1,3 +1,28 @@ +discard """ +output: ''' +false +false +false +false +false +false +false +false +false +false +128 +1 +2 +3 +4 +255 +17 +45 +19000 +4294967288 +''' +""" + # implements and tests an efficient radix tree ## another method to store an efficient array of pointers: diff --git a/tests/misc/treadln.nim b/tests/misc/treadln.nim index 6e01097aa..b716c4711 100644 --- a/tests/misc/treadln.nim +++ b/tests/misc/treadln.nim @@ -1,3 +1,11 @@ + +discard """ +output: ''' +test the improved readline handling that does not care whether its +Macintosh, Unix or Windows text format. +''' +""" + # test the improved readline handling that does not care whether its # Macintosh, Unix or Windows text format. @@ -8,5 +16,6 @@ var if open(inp, "tests/misc/treadln.nim"): while not endOfFile(inp): line = readLine(inp) - echo("#" & line & "#") + if line.len >= 2 and line[0] == '#' and line[1] == ' ': + echo line[2..^1] close(inp) diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim deleted file mode 100644 index e68b8933d..000000000 --- a/tests/misc/treadx.nim +++ /dev/null @@ -1,13 +0,0 @@ - -when not defined(windows): - import posix - - var inp = "" - var buf: array[0..10, char] - while true: - var r = read(0, addr(buf), sizeof(buf)-1) - add inp, $cstring(addr buf) - if r != sizeof(buf)-1: break - - echo inp - #dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd diff --git a/tests/misc/tshadow_magic_type.nim b/tests/misc/tshadow_magic_type.nim index 03c83079e..6f9716bb9 100644 --- a/tests/misc/tshadow_magic_type.nim +++ b/tests/misc/tshadow_magic_type.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +mylist +''' +""" + + type TListItemType* = enum RedisNil, RedisString @@ -15,7 +22,8 @@ proc seq*() = proc lrange*(key: string): TRedisList = var foo: TListItem - foo.kind = RedisNil + foo.kind = RedisString + foo.str = key result = @[foo] when isMainModule: diff --git a/tests/misc/tsizeof2.nim b/tests/misc/tsizeof2.nim index 67379871d..4252142d7 100644 --- a/tests/misc/tsizeof2.nim +++ b/tests/misc/tsizeof2.nim @@ -9,3 +9,7 @@ type const i = sizeof(MyStruct) echo i + +# bug #9868 +proc foo(a: SomeInteger): array[sizeof(a), byte] = + discard diff --git a/tests/misc/tstrace.nim b/tests/misc/tstrace.nim index 23590d958..00af0af69 100644 --- a/tests/misc/tstrace.nim +++ b/tests/misc/tstrace.nim @@ -1,3 +1,23 @@ +discard """ +exitcode: 1 +output: ''' +Traceback (most recent call last) +tstrace.nim(36) tstrace +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(31) recTest +SIGSEGV: Illegal storage access. (Attempt to read from nil?) +''' +""" + # Test the new stacktraces (great for debugging!) {.push stack_trace: on.} diff --git a/tests/misc/tstrdist.nim b/tests/misc/tstrdist.nim index 3e1939e73..53ace2fae 100644 --- a/tests/misc/tstrdist.nim +++ b/tests/misc/tstrdist.nim @@ -23,4 +23,4 @@ proc editDistance(a, b: string): int = c[(i-1)*n + (j-1)] = min(x,min(y,z)) return c[n*m] -write(stdout, editDistance("abc", "abd")) +doAssert editDistance("abc", "abd") == 3 diff --git a/tests/misc/tunsigned64mod.nim b/tests/misc/tunsigned64mod.nim index 9c9e01c45..ca3286df3 100644 --- a/tests/misc/tunsigned64mod.nim +++ b/tests/misc/tunsigned64mod.nim @@ -12,13 +12,13 @@ let t4 = (v2 mod 2'u64).uint64 # works # bug #2550 var x: uint # doesn't work -echo x mod 2 == 0 +doAssert x mod 2 == 0 var y: uint64 # doesn't work -echo y mod 2 == 0 +doAssert y mod 2 == 0 var z: uint32 # works -echo z mod 2 == 0 +doAssert z mod 2 == 0 var a: int # works -echo a mod 2 == 0 +doAssert a mod 2 == 0 diff --git a/tests/misc/tvarious.nim b/tests/misc/tvarious.nim index 8124b3fc7..191107a87 100644 --- a/tests/misc/tvarious.nim +++ b/tests/misc/tvarious.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # Test various aspects # bug #572 diff --git a/tests/modules/t8665.nim b/tests/modules/t8665.nim index 51538df79..74d31452f 100644 --- a/tests/modules/t8665.nim +++ b/tests/modules/t8665.nim @@ -1 +1,5 @@ +discard """ + action: compile +""" + import treorder diff --git a/tests/modules/texport2.nim b/tests/modules/texport2.nim index 6e55873c5..e90c58673 100644 --- a/tests/modules/texport2.nim +++ b/tests/modules/texport2.nim @@ -1,9 +1,16 @@ +discard """ +output: ''' +abc +xyz +B.foo +''' +""" + # bug #1595, #1612 import mexport2a proc main() = - echo "Import Test, two lines should follow. One with abc and one with xyz." printAbc() printXyz() diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index 52ea841ee..f332cd6d4 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -1,7 +1,7 @@ discard """ cmd: "nim default $file" output: '''hello world! 0.5''' - msg: '''[NimScript] exec: gcc -v''' + nimout: '''[NimScript] exec: gcc -v''' """ when not defined(definedefine): diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim b/tests/niminaction/Chapter3/ChatApp/src/client.nim index 4d139655c..d479ebf43 100644 --- a/tests/niminaction/Chapter3/ChatApp/src/client.nim +++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, threadpool, asyncdispatch, asyncnet import protocol diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim index 8c572aeb0..31da74d16 100644 --- a/tests/niminaction/Chapter3/ChatApp/src/server.nim +++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import asyncdispatch, asyncnet type @@ -81,4 +85,4 @@ when isMainModule: echo("Server initialised!") # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the # asyncdispatch event loop until the ``loop`` procedure finishes executing. - waitFor loop(server) \ No newline at end of file + waitFor loop(server) diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim index 478229b00..7b2776d70 100644 --- a/tests/niminaction/Chapter3/various3.nim +++ b/tests/niminaction/Chapter3/various3.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +Future is no longer empty, 42 +''' +""" + import threadpool proc foo: string = "Dog" var x: FlowVar[string] = spawn foo() @@ -33,9 +39,9 @@ let data = """ {"username": "Dominik"} """ -let obj = parseJson(data) -assert obj.kind == JObject -assert obj["username"].kind == JString +let obj = parseJson(data) +assert obj.kind == JObject +assert obj["username"].kind == JString assert obj["username"].str == "Dominik" block: @@ -60,12 +66,12 @@ var amy = Human(name: "Amy", age: 20) import asyncdispatch -var future = newFuture[int]() -doAssert(not future.finished) +var future = newFuture[int]() +doAssert(not future.finished) -future.callback = - proc (future: Future[int]) = - echo("Future is no longer empty, ", future.read) +future.callback = + proc (future: Future[int]) = + echo("Future is no longer empty, ", future.read) future.complete(42) @@ -85,9 +91,8 @@ import asyncdispatch, asyncfile, os proc readFiles() {.async.} = # --- Changed to getTempDir here. var file = openAsync(getTempDir() / "test.txt", fmReadWrite) - let data = await file.readAll() - echo(data) - await file.write("Hello!\n") + let data = await file.readAll() + echo(data) + await file.write("Hello!\n") waitFor readFiles() - diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim index 478f533d9..f20e21f4d 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils, threadpool diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim index 8df3b6aeb..dbd635634 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils, threadpool, re diff --git a/tests/niminaction/Chapter6/WikipediaStats/naive.nim b/tests/niminaction/Chapter6/WikipediaStats/naive.nim index ed4fba8e2..ce995efaf 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/naive.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/naive.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim index 7181145e9..74857367a 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, parseutils, threadpool, strutils type @@ -69,4 +73,4 @@ proc readPageCounts(filename: string, chunkSize = 1_000_000) = when isMainModule: const file = "pagecounts-20160101-050000" let filename = getCurrentDir() / file - readPageCounts(filename) \ No newline at end of file + readPageCounts(filename) diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim index c62b2f93e..db68aeb5c 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import threadpool var counter = 0 @@ -10,4 +14,4 @@ proc increment(x: int) = spawn increment(10_000) spawn increment(10_000) sync() -echo(counter) \ No newline at end of file +echo(counter) diff --git a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim index 25ad7d5f4..102dd15d3 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, parseutils proc parse(line: string, domainCode, pageTitle: var string, @@ -31,4 +35,4 @@ proc readPageCounts(filename: string) = when isMainModule: const file = "pagecounts-20160101-050000" let filename = getCurrentDir() / file - readPageCounts(filename) \ No newline at end of file + readPageCounts(filename) diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim index c7aee1b44..a7d4ebe00 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim @@ -1,6 +1,10 @@ +discard """ +output: "Database created successfully!" +""" + import database var db = newDatabase() db.setup() echo("Database created successfully!") -db.close() \ No newline at end of file +db.close() diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index b8a36306e..12aaf49b8 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import asyncdispatch, times import jester diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim index 926ca452c..da69a004c 100644 --- a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim +++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "All tests finished successfully!" +""" + import database, os, times when isMainModule: diff --git a/tests/niminaction/Chapter8/canvas/canvas.nim b/tests/niminaction/Chapter8/canvas/canvas.nim index 713d1e9e2..ae2765630 100644 --- a/tests/niminaction/Chapter8/canvas/canvas.nim +++ b/tests/niminaction/Chapter8/canvas/canvas.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import dom type diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim index a572d5231..a49e08911 100644 --- a/tests/niminaction/Chapter8/sdl/sdl_test.nim +++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os import sdl diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim index 7d56d0903..e71060cb4 100644 --- a/tests/niminaction/Chapter8/sfml/sfml_test.nim +++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim @@ -1,5 +1,6 @@ discard """ - disabled: "windows" +action: compile +disabled: "windows" """ import sfml, os diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim index c766adde0..817c1fcda 100644 --- a/tests/objects/tobjcov.nim +++ b/tests/objects/tobjcov.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # Covariance is not type safe: type @@ -14,4 +18,3 @@ proc bp(x: var TB) = x.b[high(x.b)] = -1 var f = cast[proc (x: var TA) {.nimcall.}](bp) var a: TA f(a) # bp expects a TB, but gets a TA - diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index cdb8f80db..61ef7442e 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -1,3 +1,7 @@ +discard """ +output: "[Suite] object basic methods" +""" + import unittest type Obj = object diff --git a/tests/objects/tobjects.nim b/tests/objects/tobjects.nim deleted file mode 100644 index 66a38960e..000000000 --- a/tests/objects/tobjects.nim +++ /dev/null @@ -1,52 +0,0 @@ -type - TBase = object of RootObj - x, y: int - - TSubclassKind = enum ka, kb, kc, kd, ke, kf - TSubclass = object of TBase - case c: TSubclassKind - of ka, kb, kc, kd: - a, b: int - of ke: - d, e, f: char - else: nil - n: bool - -type - TMyObject = object of RootObj - case disp: range[0..4] - of 0: arg: char - of 1: s: string - else: wtf: bool - -var - x: TMyObject - -var - global: int - -var - s: string - r: float = 0.0 - i: int = 500 + 400 - -case i -of 500..999: write(stdout, "ha!\n") -of 1000..3000, 12: write(stdout, "ganz schön groß\n") -of 1, 2, 3: write(stdout, "1 2 oder 3\n") -else: write(stdout, "sollte nicht passieren\n") - -case readLine(stdin) -of "Rumpf": write(stdout, "Hallo Meister!\n") -of "Andreas": write(stdout, "Hallo Meister!\n") -else: write(stdout, "Nicht mein Meister!\n") - -global = global + 1 -write(stdout, "Hallo wie heißt du? \n") -s = readLine(stdin) -i = 0 -while i < len(s): - if s[i] == 'c': write(stdout, "'c' in deinem Namen gefunden\n") - i = i + 1 - -write(stdout, "Du heißt " & s) diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim index a7f232c5b..69b099f24 100644 --- a/tests/objvariant/tcheckedfield1.nim +++ b/tests/objvariant/tcheckedfield1.nim @@ -1,6 +1,8 @@ discard """ - msg: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" + nimout: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" line:51 + action: run + output: "abc abc" """ import strutils diff --git a/tests/osproc/ta_in.nim b/tests/osproc/ta_in.nim index b46890f6e..fb294ec14 100644 --- a/tests/osproc/ta_in.nim +++ b/tests/osproc/ta_in.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # This file is prefixed with an "a", because other tests # depend on it and it must be compiled first. import strutils diff --git a/tests/osproc/ta_out.nim b/tests/osproc/ta_out.nim index f7091a7f6..318a27d59 100644 --- a/tests/osproc/ta_out.nim +++ b/tests/osproc/ta_out.nim @@ -1,3 +1,14 @@ +discard """ +output: ''' +to stdout +to stdout +to stderr +to stderr +to stdout +to stdout +''' +""" + # This file is prefixed with an "a", because other tests # depend on it and it must be compiled first. stdout.writeLine("to stdout") diff --git a/tests/osproc/tafalse.nim b/tests/osproc/tafalse.nim index 24fd4fb2e..05a0bfce9 100644 --- a/tests/osproc/tafalse.nim +++ b/tests/osproc/tafalse.nim @@ -1,3 +1,7 @@ +discard """ +exitcode: 1 +""" + # 'tafalse.nim' to ensure it is compiled before texitcode.nim import system quit(QuitFailure) diff --git a/tests/osproc/texitcode.nim b/tests/osproc/texitcode.nim index 4eaab6da2..6dc5508b5 100644 --- a/tests/osproc/texitcode.nim +++ b/tests/osproc/texitcode.nim @@ -2,6 +2,7 @@ discard """ file: "texitcode.nim" output: "" """ + import osproc, os const filename = when defined(Windows): "tafalse.exe" else: "tafalse" diff --git a/tests/overload/tselfderef.nim b/tests/overload/tselfderef.nim index 708e4043b..96f1da42a 100644 --- a/tests/overload/tselfderef.nim +++ b/tests/overload/tselfderef.nim @@ -1,7 +1,10 @@ +discard """ +action: compile +""" + # bug #4671 {.experimental.} {.this: self.} - type SomeObj = object f: int diff --git a/tests/parallel/tarray_of_channels.nim b/tests/parallel/tarray_of_channels.nim index 90ae8369c..e2a682bd5 100644 --- a/tests/parallel/tarray_of_channels.nim +++ b/tests/parallel/tarray_of_channels.nim @@ -1,3 +1,15 @@ +discard """ +sortoutput: true +output: ''' +(x: 0.0) +(x: 0.0) +(x: 0.0) +test +test +test +''' +""" + # bug #2257 import threadpool diff --git a/tests/parallel/tdont_be_stupid.nim b/tests/parallel/tdont_be_stupid.nim index a7e82466a..d765c11a9 100644 --- a/tests/parallel/tdont_be_stupid.nim +++ b/tests/parallel/tdont_be_stupid.nim @@ -1,3 +1,11 @@ +discard """ +output: ''' +100 +200 +300 +400 +''' +""" import threadpool, os @@ -12,4 +20,4 @@ proc sleepsort(nums: openArray[int]) = spawn single(nums[i]) i += 1 -sleepsort([50,3,40,25]) +sleepsort([400,100,300,200]) diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim index c7972d225..b1eb7e7c5 100644 --- a/tests/parallel/tguard1.nim +++ b/tests/parallel/tguard1.nim @@ -1,3 +1,7 @@ +discard """ +output: "90" +""" + when false: template lock(a, b: ptr Lock; body: stmt) = diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim index 463ee1a47..62341d8f0 100644 --- a/tests/parallel/tlet_spawn.nim +++ b/tests/parallel/tlet_spawn.nim @@ -1,3 +1,8 @@ +discard """ +output: ''' +done999 999 +''' +""" import threadpool diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim index 53481e4df..45fdf0f8f 100644 --- a/tests/parallel/tmissing_deepcopy.nim +++ b/tests/parallel/tmissing_deepcopy.nim @@ -1,5 +1,6 @@ discard """ - ccodeCheck: "\\i @'deepCopy(' .*" + ccodeCheck: "@'genericDeepCopy(' .*" + action: compile """ # bug #2286 diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim index 9874d3299..ee9508074 100644 --- a/tests/parallel/tsimple_array_checks.nim +++ b/tests/parallel/tsimple_array_checks.nim @@ -1,3 +1,25 @@ +discard """ +sortoutput: true +output: ''' +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +Hello 1 +Hello 2 +Hello 3 +Hello 4 +Hello 5 +Hello 6 +''' +""" + # bug #2287 import threadPool diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim index 50b427d71..6d0466df3 100644 --- a/tests/pragmas/tnoreturn.nim +++ b/tests/pragmas/tnoreturn.nim @@ -1,5 +1,6 @@ discard """ ccodeCheck: "\\i @'__attribute__((noreturn))' .*" +action: compile """ proc noret1*(i: int) {.noreturn.} = diff --git a/tests/seq/tseq.nim b/tests/seq/tseq.nim index 6528d518e..1cb94b308 100644 --- a/tests/seq/tseq.nim +++ b/tests/seq/tseq.nim @@ -170,6 +170,30 @@ block tshallowseq: xxx() +block tshallowemptyseq: + proc test() = + var nilSeq: seq[int] = @[] + var emptySeq: seq[int] = newSeq[int]() + block: + var t = @[1,2,3] + shallow(nilSeq) + t = nilSeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallow(emptySeq) + t = emptySeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, nilSeq) + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, emptySeq) + doAssert t == @[] + test() + import strutils block ttoseq: diff --git a/tests/stdlib/osproctest.nim b/tests/stdlib/osproctest.nim new file mode 100644 index 000000000..8c4fba9ba --- /dev/null +++ b/tests/stdlib/osproctest.nim @@ -0,0 +1,8 @@ +# This is test program for the osproc module. + +import os + +echo getCurrentDir() + +for i in 1..paramCount(): + echo paramStr(i) diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim new file mode 100644 index 000000000..23b8b82ca --- /dev/null +++ b/tests/stdlib/tcgi.nim @@ -0,0 +1,23 @@ +discard """ + action: run + file: tcgi.nim + output: "[Suite] Test cgi module" +""" + +import unittest +import cgi, strtabs + +suite "Test cgi module": + const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" + + test "test query parsing with readData": + let parsedQuery = readData(queryString) + + check parsedQuery["foo"] == "bar" + check parsedQuery["фу"] == "бар" + check parsedQuery["checked"] == "✓" + check parsedQuery["list"] == "1,2,3" + check parsedQuery["with_space"] == "text with space" + + expect KeyError: + discard parsedQuery["not_existing_key"] diff --git a/tests/stdlib/tcputime.nim b/tests/stdlib/tcputime.nim deleted file mode 100644 index b0cc19c6c..000000000 --- a/tests/stdlib/tcputime.nim +++ /dev/null @@ -1,11 +0,0 @@ -import times, os - -var e = epochTime() -var c = cpuTime() - -os.sleep(1000) - -e = epochTime() - e -c = cpuTime() - c - -echo "epochTime: ", e, " cpuTime: ", c diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index c442b43fb..2cd1e4e47 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,14 @@ -import unittest -import hashes + +discard """ +output: ''' +[Suite] hashes + +[Suite] hashing + +''' +""" + +import unittest, hashes suite "hashes": suite "hashing": diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index 9f99df93a..889c734c5 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,3 +1,6 @@ +discard """ +output: "[Suite] httpcore" +""" import unittest diff --git a/tests/stdlib/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim index ec90e580d..1091d72cd 100644 --- a/tests/stdlib/tjsonexternproc.nim +++ b/tests/stdlib/tjsonexternproc.nim @@ -1,5 +1,11 @@ +discard """ +output: ''' +{"data":[1]} +''' +""" + # Test case for https://github.com/nim-lang/Nim/issues/6385 import mjsonexternproc # import json -foo(1) \ No newline at end of file +foo(1) diff --git a/tests/stdlib/tjsontestsuite.nim b/tests/stdlib/tjsontestsuite.nim index 06f783a73..db31963fd 100644 --- a/tests/stdlib/tjsontestsuite.nim +++ b/tests/stdlib/tjsontestsuite.nim @@ -1,3 +1,7 @@ +discard """ +disabled: true +""" + ## JSON tests based on https://github.com/nst/JSONTestSuite import unittest, diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim deleted file mode 100644 index eb0506f5f..000000000 --- a/tests/stdlib/tmath2.nim +++ /dev/null @@ -1,85 +0,0 @@ -# tests for the interpreter - -proc loops(a: var int) = - discard - #var - # b: int - #b = glob - #while b != 0: - # b = b + 1 - #a = b - -proc mymax(a, b: int): int = - #loops(result) - result = a - if b > a: result = b - -proc test(a, b: int) = - var - x, y: int - x = 0 - y = 7 - if x == a + b * 3 - 7 or - x == 8 or - x == y and y > -56 and y < 699: - y = 0 - elif y == 78 and x == 0: - y = 1 - elif y == 0 and x == 0: - y = 2 - else: - y = 3 - -type - TTokType = enum - tkNil, tkType, tkConst, tkVar, tkSymbol, tkIf, - tkWhile, tkFor, tkLoop, tkCase, tkLabel, tkGoto - -proc testCase(t: TTokType): int = - case t - of tkNil, tkType, tkConst: result = 0 - of tkVar: result = 1 - of tkSymbol: result = 2 - of tkIf..tkFor: result = 3 - of tkLoop: result = 56 - else: result = -1 - test(0, 9) # test the call - -proc TestLoops() = - var - i, j: int - - while i >= 0: - if i mod 3 == 0: - break - i = i + 1 - while j == 13: - j = 13 - break - break - - while true: - break - - -var - glob: int - a: array[0..5, int] - -proc main() = - #glob = 0 - #loops( glob ) - var - res: int - s: string - #write(stdout, mymax(23, 45)) - write(stdout, "Hallo! Wie heisst du? ") - s = readLine(stdin) - # test the case statement - case s - of "Andreas": write(stdout, "Du bist mein Meister!\n") - of "Rumpf": write(stdout, "Du bist in der Familie meines Meisters!\n") - else: write(stdout, "ich kenne dich nicht!\n") - write(stdout, "Du heisst " & s & "\n") - -main() diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 8b66dfcc1..a18fba083 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,5 +1,6 @@ discard """ file: "tmemfiles1.nim" + outputsub: "" """ import memfiles, os var @@ -8,5 +9,5 @@ var # Create a new file mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) mm.close() -mm.close() +# mm.close() if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemlines.nim b/tests/stdlib/tmemlines.nim index c850b5493..98e03b5bb 100644 --- a/tests/stdlib/tmemlines.nim +++ b/tests/stdlib/tmemlines.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import memfiles var inp = memfiles.open("tests/stdlib/tmemlines.nim") for line in lines(inp): diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 9fa68cf02..97ad751ee 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,6 +1,15 @@ +discard """ +output: "15" +disabled: "appveyor" +""" + import memfiles var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") var buffer: TaintedString = "" +var lineCount = 0 for line in lines(inp, buffer): - echo("#" & line & "#") + lineCount += 1 + close(inp) + +echo lineCount diff --git a/tests/stdlib/tmemslices.nim b/tests/stdlib/tmemslices.nim index d724254a2..c0d6d3960 100644 --- a/tests/stdlib/tmemslices.nim +++ b/tests/stdlib/tmemslices.nim @@ -1,3 +1,9 @@ +discard """ +outputsub: "rlwuiadtrnzb" +""" + +# chatever the sub pattern it will find itself + import memfiles var inp = memfiles.open("tests/stdlib/tmemslices.nim") for mem in memSlices(inp): diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim index c683647bc..c2738b8a5 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import nativesockets, unittest suite "nativesockets": @@ -5,4 +9,3 @@ suite "nativesockets": let hostname = getHostname() check hostname.len > 0 check hostname.len < 64 - diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 009561272..2dd22796c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import net, nativesockets import unittest diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim new file mode 100644 index 000000000..ac129e709 --- /dev/null +++ b/tests/stdlib/tosproc.nim @@ -0,0 +1,24 @@ +discard """ + file: "tospaths.nim" + output: "" +""" +# test the osproc module + +import os, osproc + +block execProcessTest: + let dir = parentDir(currentSourcePath()) + let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim")) + doAssert err == 0 + let exePath = dir / addFileExt("osproctest", ExeExt) + let outStr1 = execProcess(exePath, workingDir=dir, args=["foo", "b A r"], options={}) + doAssert outStr1 == dir & "\nfoo\nb A r\n" + + const testDir = "t e st" + createDir(testDir) + doAssert dirExists(testDir) + let outStr2 = execProcess(exePath, workingDir=testDir, args=["x yz"], options={}) + doAssert outStr2 == absolutePath(testDir) & "\nx yz\n" + + removeDir(testDir) + removeFile(exePath) diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim index 7fc6c5d85..a46d91d68 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "SUCCESS" +""" + import os, osproc when defined(Windows): diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index 848fba2da..948bc8d5f 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -1,4 +1,10 @@ -# Test the new parseopt module +discard """ +disabled: true +""" + +# this file has a type in the name, and it does not really test +# parseopt module, because tester has no support to set arguments. Test the +# new parseopt module. Therefore it is disabled. import parseopt diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index 57a43f99e..14f1fd6e2 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + # Test Posix interface when not defined(windows): @@ -13,4 +17,3 @@ when not defined(windows): writeLine(stdout, u.nodename) writeLine(stdout, u.release) writeLine(stdout, u.machine) - diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim index d18b468c8..4f8d5fb20 100644 --- a/tests/stdlib/tquit.nim +++ b/tests/stdlib/tquit.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +just exiting... +''' +""" + # Test the new beforeQuit variable: proc myExit() {.noconv.} = diff --git a/tests/stdlib/trepr2.nim b/tests/stdlib/trepr2.nim index 300df565d..89379da96 100644 --- a/tests/stdlib/trepr2.nim +++ b/tests/stdlib/trepr2.nim @@ -1,3 +1,8 @@ +discard """ +outputsub: "" +""" + +# output not testable because repr prints pointer adresses # test the new "repr" built-in proc type diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index c702ccc2a..fd89f68af 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + # tests for rstgen module. import ../../lib/packages/docutils/rstgen @@ -27,7 +31,7 @@ suite "YAML syntax highlighting": <span class="Punctuation">?</span> <span class="StringLit">key</span> <span class="Punctuation">:</span> <span class="StringLit">value</span> <span class="Keyword">...</span></pre>""" - + test "Block scalars": let input = """.. code-block:: yaml a literal block scalar: | @@ -55,7 +59,7 @@ suite "YAML syntax highlighting": <span class="StringLit">another literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|+</span> <span class="Comment"># comment after header</span><span class="LongStringLit"> allowed, since more indented than parent</span></pre>""" - + test "Directives": let input = """.. code-block:: yaml %YAML 1.2 @@ -97,7 +101,7 @@ suite "YAML syntax highlighting": <span class="StringLit">more numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span><span class="DecNumber">-783</span><span class="Punctuation">,</span> <span class="FloatNumber">11e78</span><span class="Punctuation">]</span><span class="Punctuation">,</span> <span class="StringLit">not numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span> <span class="StringLit">42e</span><span class="Punctuation">,</span> <span class="StringLit">0023</span><span class="Punctuation">,</span> <span class="StringLit">+32.37</span><span class="Punctuation">,</span> <span class="StringLit">8 ball</span><span class="Punctuation">]</span> <span class="Punctuation">}</span></pre>""" - + test "Anchors, Aliases, Tags": let input = """.. code-block:: yaml --- !!map @@ -136,4 +140,4 @@ suite "YAML syntax highlighting": <span class="DecNumber">-3</span> <span class="DecNumber">-4</span> <span class="StringLit">example.com/not/a#comment</span><span class="Punctuation">:</span> - <span class="StringLit">?not a map key</span></pre>""" \ No newline at end of file + <span class="StringLit">?not a map key</span></pre>""" diff --git a/tests/stdlib/tsortcall.nim b/tests/stdlib/tsortcall.nim index 45b98805f..242e3fe4c 100644 --- a/tests/stdlib/tsortcall.nim +++ b/tests/stdlib/tsortcall.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import algorithm import unittest @@ -40,7 +44,7 @@ suite "test sort, sorted, and isSorted procs": test "test the shortcut versions with descending sort order": check(not unSortedIntSeq.isSorted(SortOrder.Descending)) check sorted(unSortedIntSeq, SortOrder.Descending) == reversed sortedIntSeq - check sorted(unSortedIntSeq).isSorted(SortOrder.Descending) + check sorted(unSortedIntSeq).isSorted(SortOrder.Ascending) unSortedIntSeq.sort(SortOrder.Descending) check unSortedIntSeq == reversed sortedIntSeq diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 16dbc0e1b..559824d85 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,12 +1,25 @@ +discard """ +input: "Arne" +output: ''' +Hello! What is your name? +Nice name: Arne +fs is: nil + +threw exception +''' +disabled: "windows" +""" + + import streams block tstreams: var outp = newFileStream(stdout) var inp = newFileStream(stdin) - write(outp, "Hello! What is your name?") + writeLine(outp, "Hello! What is your name?") var line = readLine(inp) - write(outp, "Nice name: " & line) + writeLine(outp, "Nice name: " & line) block tstreams2: diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index a248cc3b2..18ed57167 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -1,3 +1,92 @@ +discard """ +sortoutput: true +output: ''' +key1: value1 +key2: value2 +key_0: value0 +key_10: value10 +key_11: value11 +key_12: value12 +key_13: value13 +key_14: value14 +key_15: value15 +key_16: value16 +key_17: value17 +key_18: value18 +key_19: value19 +key_20: value20 +key_21: value21 +key_22: value22 +key_23: value23 +key_24: value24 +key_25: value25 +key_26: value26 +key_27: value27 +key_28: value28 +key_29: value29 +key_30: value30 +key_31: value31 +key_32: value32 +key_33: value33 +key_34: value34 +key_35: value35 +key_36: value36 +key_37: value37 +key_38: value38 +key_39: value39 +key_3: value3 +key_40: value40 +key_41: value41 +key_42: value42 +key_43: value43 +key_44: value44 +key_45: value45 +key_46: value46 +key_47: value47 +key_48: value48 +key_49: value49 +key_4: value4 +key_50: value50 +key_51: value51 +key_52: value52 +key_53: value53 +key_54: value54 +key_55: value55 +key_56: value56 +key_57: value57 +key_58: value58 +key_59: value59 +key_5: value5 +key_60: value60 +key_61: value61 +key_62: value62 +key_63: value63 +key_64: value64 +key_65: value65 +key_66: value66 +key_67: value67 +key_68: value68 +key_69: value69 +key_6: value6 +key_70: value70 +key_71: value71 +key_72: value72 +key_73: value73 +key_74: value74 +key_75: value75 +key_76: value76 +key_77: value77 +key_78: value78 +key_79: value79 +key_7: value7 +key_80: value80 +key_8: value8 +key_9: value9 +length of table 81 +value1 = value2 +''' +""" + import strtabs var tab = newStringTable({"key1": "val1", "key2": "val2"}, @@ -9,4 +98,4 @@ for key, val in pairs(tab): writeLine(stdout, key, ": ", val) writeLine(stdout, "length of table ", $tab.len) -writeLine(stdout, `%`("$key1 = $key2; ${PATH}", tab, {useEnvironment})) +writeLine(stdout, `%`("$key1 = $key2", tab, {useEnvironment})) diff --git a/tests/stdlib/twalker.nim b/tests/stdlib/twalker.nim deleted file mode 100644 index 91c97df01..000000000 --- a/tests/stdlib/twalker.nim +++ /dev/null @@ -1,13 +0,0 @@ -# iterate over all files with a given filter: - -import - "../../lib/pure/os.nim", ../../ lib / pure / times - -proc main(filter: string) = - for filename in walkFiles(filter): - writeLine(stdout, filename) - - for key, val in envPairs(): - writeLine(stdout, key & '=' & val) - -main("*.nim") diff --git a/tests/system/t7894.nim b/tests/system/t7894.nim index 2808e5020..27ee3f220 100644 --- a/tests/system/t7894.nim +++ b/tests/system/t7894.nim @@ -1,18 +1,24 @@ discard """ +disabled: "travis" +disabled: "appveyor" """ -import os +# CI integration servers are out of memory for this test const size = 250000000 -var saved = newSeq[seq[int8]]() -for i in 0..22: - # one of these is 0.25GB. - #echo i - var x = newSeq[int8](size) - sleep(10) - saved.add(x) +proc main() = -for x in saved: - #echo x.len - doAssert x.len == size + var saved = newSeq[seq[int8]]() + + for i in 0..22: + # one of these is 0.25GB. + #echo i + var x = newSeq[int8](size) + saved.add(x) + + for x in saved: + #echo x.len + doAssert x.len == size + +main() diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim index bf2cd97a8..9b970fda0 100644 --- a/tests/system/talloc.nim +++ b/tests/system/talloc.nim @@ -1,6 +1,9 @@ discard """ +disabled: "appveyor" """ +# appveyor is "out of memory" + var x: ptr int x = cast[ptr int](alloc(7)) diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim index 0757c0724..e40c3f93c 100644 --- a/tests/system/talloc2.nim +++ b/tests/system/talloc2.nim @@ -1,6 +1,9 @@ discard """ +disabled: "windows" """ +# appveyor is "out of memory" + const nmax = 2*1024*1024*1024 diff --git a/tests/system/tio.nim b/tests/system/tio.nim index 7e9e18950..c4d041560 100644 --- a/tests/system/tio.nim +++ b/tests/system/tio.nim @@ -1,14 +1,19 @@ discard """ +outputsub: "" +disabled: true """ import - unittest, osproc, streams, os, strformat + unittest, osproc, streams, os, strformat, strutils const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + const TEST_FILE = "tests/testdata/string.txt" proc echoLoop(str: string): string = result = "" - var process = startProcess(findExe("tests/system/helpers/readall_echo")) + let exe = findExe("tests/system/helpers/readall_echo") + echo "exe: ", exe + var process = startProcess(exe) var input = process.inputStream input.write(str) input.close() @@ -22,10 +27,9 @@ suite "io": test "stdin": check: echoLoop(STRING_DATA) == STRING_DATA - echoLoop(STRING_DATA[0..3999]) == STRING_DATA[0..3999] test "file": check: - readFile(TEST_FILE) == STRING_DATA + readFile(TEST_FILE).strip == STRING_DATA proc verifyFileSize(sz: int64) = diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim index 5e4a1b317..c1126405c 100644 --- a/tests/system/tnilconcats.nim +++ b/tests/system/tnilconcats.nim @@ -23,3 +23,10 @@ when true: doAssert s == "fooabc" echo x + + # casting an empty string as sequence with shallow() should not segfault + var s2: string + shallow(s2) + s2 &= "foo" + doAssert s2 == "foo" + diff --git a/tests/system/tparams.nim b/tests/system/tparams.nim index dd5511b8f..015530043 100644 --- a/tests/system/tparams.nim +++ b/tests/system/tparams.nim @@ -1,6 +1,3 @@ -discard """ -""" - import os import osproc import parseopt2 @@ -13,7 +10,6 @@ if argv == @[]: doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0 else: let f = toSeq(getopt()) - echo f.repr doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == "" doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a" doAssert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d" diff --git a/tests/template/tconfusinglocal.nim b/tests/template/tconfusinglocal.nim index 50bf8f4b2..9f641e2bf 100644 --- a/tests/template/tconfusinglocal.nim +++ b/tests/template/tconfusinglocal.nim @@ -1,3 +1,7 @@ +discard """ +output: "0" +""" + # bug #5135 proc fail*[E](e: E): void = diff --git a/tests/template/texponential_eval.nim b/tests/template/texponential_eval.nim index 32af9e8f7..b4e3faefb 100644 --- a/tests/template/texponential_eval.nim +++ b/tests/template/texponential_eval.nim @@ -1,13 +1,17 @@ # bug #1940 discard """ - nimout: '''=== +nimout: ''' +=== merge (A) with (B) merge (A B) with (C) merge (A B C) with (D) merge (A B C D) with (E) merge (A B C D E) with (F) -===''' +=== +''' + +output: "A B C D E F" """ type SqlStmt = tuple diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim index de40450aa..506f57148 100644 --- a/tests/template/thygienictempl.nim +++ b/tests/template/thygienictempl.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + var e = "abc" diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 3fb0dd4a5..da86d63dc 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -1,4 +1,15 @@ - +discard """ +output: ''' +0 +1 +2 +3 +0 +1 +2 +3 +''' +""" # bug #1915 import macros diff --git a/tests/template/tsighash_regression.nim b/tests/template/tsighash_regression.nim index bf1f4dfe4..f3a6b4833 100644 --- a/tests/template/tsighash_regression.nim +++ b/tests/template/tsighash_regression.nim @@ -1,5 +1,8 @@ +discard """ +exitcode: 1 +outputsub: "0" +""" import tconfusinglocal - fail "foo" diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim index d6a369d32..943cdae05 100644 --- a/tests/template/ttempl3.nim +++ b/tests/template/ttempl3.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + template withOpenFile(f: untyped, filename: string, mode: FileMode, actions: untyped): untyped = diff --git a/tests/testament/tshouldfail.nim b/tests/testament/tshouldfail.nim new file mode 100644 index 000000000..02e4bfd80 --- /dev/null +++ b/tests/testament/tshouldfail.nim @@ -0,0 +1,17 @@ +discard """ +cmd: "testament/tester --directory:testament --colors:off --nim:../compiler/nim category shouldfail" +action: compile +nimout: ''' +FAIL: tccodecheck.nim C +FAIL: tcolumn.nim C +FAIL: terrormsg.nim C +FAIL: texitcode1.nim C +FAIL: tfile.nim C +FAIL: tline.nim C +FAIL: tmaxcodesize.nim C +FAIL: tnimout.nim C +FAIL: toutput.nim C +FAIL: toutputsub.nim C +FAIL: tsortoutput.nim C +''' +""" diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim index 20ebe5ecc..9e2b2fb86 100644 --- a/tests/typerel/tnoargopenarray.nim +++ b/tests/typerel/tnoargopenarray.nim @@ -1,7 +1,8 @@ +discard """ +action: compile +""" import db_sqlite var db: DbConn exec(db, sql"create table blabla()") - - diff --git a/tests/typerel/trettypeinference.nim b/tests/typerel/trettypeinference.nim index fa4e89cc8..aa0d66e5b 100644 --- a/tests/typerel/trettypeinference.nim +++ b/tests/typerel/trettypeinference.nim @@ -1,5 +1,5 @@ discard """ - msg: "instantiated for string\ninstantiated for int\ninstantiated for bool" + nimout: "instantiated for string\ninstantiated for int\ninstantiated for bool" output: "int\nseq[string]\nA\nB\n100\ntrue" """ diff --git a/tests/typerel/tsecondarrayproperty.nim b/tests/typerel/tsecondarrayproperty.nim index 3a6879b16..315ad06bf 100644 --- a/tests/typerel/tsecondarrayproperty.nim +++ b/tests/typerel/tsecondarrayproperty.nim @@ -1,3 +1,7 @@ +discard """ +output: "4" +""" + type TFoo = object @@ -25,4 +29,3 @@ echo f.second[1] #echo `second[]`(f,1) # this is the only way I could use it, but not what I expected - diff --git a/tests/typerel/ttuple1.nim b/tests/typerel/ttuple1.nim index d5c80800c..a03cc510e 100644 --- a/tests/typerel/ttuple1.nim +++ b/tests/typerel/ttuple1.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +M=1000, D=500, C=100, L=50, X=10, V=5, I=1 +''' +""" + const romanNumbers = [ ("M", 1000), ("D", 500), ("C", 100), ("L", 50), ("X", 10), ("V", 5), ("I", 1) ] @@ -12,5 +18,3 @@ for key, val in items(romanNumbers): proc PrintBiTuple(t: tuple[k: string, v: int]): int = stdout.write(t.k & "=" & $t.v & ", ") return 0 - - diff --git a/tests/types/taliasbugs.nim b/tests/types/taliasbugs.nim index bdb2a7a32..f1b35edf6 100644 --- a/tests/types/taliasbugs.nim +++ b/tests/types/taliasbugs.nim @@ -1,5 +1,5 @@ discard """ - msg: '''true + nimout: '''true true true true diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim index b071c08e1..c43215d1e 100644 --- a/tests/types/tauto_canbe_void.nim +++ b/tests/types/tauto_canbe_void.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +arg +arg +''' +""" + import sugar @@ -6,4 +13,3 @@ template tempo(s) = tempo((s: string)->auto => echo(s)) tempo((s: string) => echo(s)) - diff --git a/tests/types/tfinalobj.nim b/tests/types/tfinalobj.nim index 2fda73363..6a1c8c2ce 100644 --- a/tests/types/tfinalobj.nim +++ b/tests/types/tfinalobj.nim @@ -1,5 +1,6 @@ discard """ - output: "abc" + output: '''abc +16 == 16''' """ type @@ -14,3 +15,19 @@ doAssert TA.sizeof == string.sizeof echo a.x +########################################## +# bug #9794 +########################################## +type + imported_double {.importc: "double".} = object + + Pod = object + v* : imported_double + seed*: int32 + + Pod2 = tuple[v: imported_double, seed: int32] + +proc test() = + echo sizeof(Pod), " == ",sizeof(Pod2) + +test() \ No newline at end of file diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim index f4260495a..2e0fcb888 100644 --- a/tests/vm/tconsteval.nim +++ b/tests/vm/tconsteval.nim @@ -1,4 +1,5 @@ discard """ +action: compile """ import strutils @@ -28,4 +29,3 @@ Possible Commands: CompileDate, CompileTime] echo HelpText - diff --git a/tests/vm/tconsttable2.nim b/tests/vm/tconsttable2.nim index e07734eb5..5a392fb66 100644 --- a/tests/vm/tconsttable2.nim +++ b/tests/vm/tconsttable2.nim @@ -1,5 +1,5 @@ discard """ - msg: '''61''' + nimout: '''61''' """ # bug #2297 diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim index 0eaa050da..5c323f0e7 100644 --- a/tests/vm/teval1.nim +++ b/tests/vm/teval1.nim @@ -1,3 +1,8 @@ + +discard """ +nimout: "##" +""" + import macros proc testProc: string {.compileTime.} = @@ -14,9 +19,9 @@ when true: const x = testProc() -echo "##", x, "##" +doAssert x == "" # bug #1310 static: - var i, j: set[int8] = {} - var k = i + j + var i, j: set[int8] = {} + var k = i + j diff --git a/tests/vm/tgorge.nim b/tests/vm/tgorge.nim index 694754f41..11c49a4cc 100644 --- a/tests/vm/tgorge.nim +++ b/tests/vm/tgorge.nim @@ -1,3 +1,10 @@ +discard """ +disabled: "windows" +""" + +# If your os is windows and this test fails for you locally, please +# check what is going wrong. + import os template getScriptDir(): string = diff --git a/tests/vm/tgorge.sh b/tests/vm/tgorge.sh index ba47afeae..ba47afeae 100644..100755 --- a/tests/vm/tgorge.sh +++ b/tests/vm/tgorge.sh diff --git a/tests/vm/tgorgeex.sh b/tests/vm/tgorgeex.sh index 36ba0a02f..36ba0a02f 100644..100755 --- a/tests/vm/tgorgeex.sh +++ b/tests/vm/tgorgeex.sh diff --git a/tests/vm/tinheritance.nim b/tests/vm/tinheritance.nim index 2c224abcb..a94ccafcd 100644 --- a/tests/vm/tinheritance.nim +++ b/tests/vm/tinheritance.nim @@ -1,5 +1,5 @@ discard """ - msg: '''Hello fred , managed by sally + nimout: '''Hello fred , managed by sally Hello sally , managed by bob''' """ # bug #3973 diff --git a/tests/vm/tmitems.nim b/tests/vm/tmitems.nim index a0e64d6aa..87835d1cd 100644 --- a/tests/vm/tmitems.nim +++ b/tests/vm/tmitems.nim @@ -1,5 +1,5 @@ discard """ - msg: '''13''' + nimout: '''13''' output: '''3 3 3''' diff --git a/tests/vm/tsimpleglobals.nim b/tests/vm/tsimpleglobals.nim index 27bfdce50..7ab665070 100644 --- a/tests/vm/tsimpleglobals.nim +++ b/tests/vm/tsimpleglobals.nim @@ -1,5 +1,5 @@ discard """ - msg: "abc xyz bb" + nimout: "abc xyz bb" """ # bug #2473 diff --git a/tests/vm/tslurp.nim b/tests/vm/tslurp.nim index d0041eaad..d762eb079 100644 --- a/tests/vm/tslurp.nim +++ b/tests/vm/tslurp.nim @@ -7,6 +7,6 @@ const relRes = slurp"./tslurp.nim" absRes = slurp(getScriptDir() / "tslurp.nim") -echo relRes -echo absRes - +doAssert relRes.len > 200 +doAssert absRes.len > 200 +doAssert relRes == absRes diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index 246a0211b..37bf62246 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -1,5 +1,5 @@ discard """ - msg: '''1 + nimout: '''1 2 3 1 diff --git a/tests/vm/tswap.nim b/tests/vm/tswap.nim index 2219be9ca..4243b5a71 100644 --- a/tests/vm/tswap.nim +++ b/tests/vm/tswap.nim @@ -1,5 +1,5 @@ discard """ -msg: ''' +nimout: ''' x.data = @[10] y = @[11] x.data = @[11] diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim index 5f8884e80..dd4c597ba 100644 --- a/tests/vm/ttouintconv.nim +++ b/tests/vm/ttouintconv.nim @@ -1,7 +1,7 @@ import macros discard """ -msg: ''' +nimout: ''' 8 9 17 239 255 61439 65534 65535 diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 69275548f..7cd13b2ac 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -70,6 +70,7 @@ doc/manual.rst doc/lib.rst doc/tut1.rst doc/tut2.rst +doc/tut3.rst doc/nimc.rst doc/niminst.rst doc/gc.rst @@ -82,6 +83,7 @@ doc/lib.rst doc/manual.rst doc/tut1.rst doc/tut2.rst +doc/tut3.rst doc/nimc.rst doc/overview.rst doc/filters.rst |