From 70cf34cbdcff55771b6936b898fdb87c22af403e Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 4 Dec 2011 20:14:50 +0100 Subject: [PATCH] 'assert' is now implemented without compiler magic --- compiler/ast.nim | 3 ++- compiler/ccgexprs.nim | 4 ++-- compiler/evals.nim | 3 ++- compiler/msgs.nim | 9 +++++---- compiler/semexprs.nim | 6 ++++++ compiler/semfold.nim | 4 ++++ compiler/semstmts.nim | 2 +- compiler/sigmatch.nim | 2 +- compiler/syntaxes.nim | 2 +- compiler/transf.nim | 2 +- lib/system.nim | 25 ++++++++++++++++++------- lib/system/channels.nim | 6 +++--- lib/system/ecmasys.nim | 2 +- tests/run/tuserassert.nim | 14 ++++++++++++++ todo.txt | 3 ++- web/news.txt | 3 +++ 16 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 tests/run/tuserassert.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 3d07fda075..2b84c415f6 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -370,7 +370,8 @@ type mConTArr, mConTT, mSlice, mFields, mFieldPairs, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert, + mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, + mAssert, mAstToStr, mRand, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mReset, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index caaab1ab55..13d9637ad5 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -607,7 +607,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = InternalError(e.info, "genCheckedRecordField") # generate the checks: for i in countup(1, sonsLen(e) - 1): it = e.sons[i] - assert(it.kind == nkCall) + assert(it.kind in nkCallKinds) assert(it.sons[0].kind == nkSym) op = it.sons[0].sym if op.magic == mNot: it = it.sons[1] @@ -1403,7 +1403,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) - of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit: + of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mRand: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, opr.loc.r.ropeToStr) diff --git a/compiler/evals.nim b/compiler/evals.nim index c44ea3aeb9..fff58b7f4c 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -1162,7 +1162,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = of nkEmpty: result = n of nkSym: result = evalSym(c, n, flags) of nkType..nkNilLit: result = copyNode(n) # end of atoms - of nkCall, nkHiddenCallConv, nkMacroStmt, nkCommand, nkCallStrLit: + of nkCall, nkHiddenCallConv, nkMacroStmt, nkCommand, nkCallStrLit, nkInfix, + nkPrefix, nkPostfix: result = evalMagicOrCall(c, n) of nkCurly, nkBracket, nkRange: # flags need to be passed here for mNAddMultiple :-( diff --git a/compiler/msgs.nim b/compiler/msgs.nim index d3ab9a9897..3090198acd 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -412,6 +412,7 @@ const PosErrorFormat* = "$1($2, $3) Error: $4" PosWarningFormat* = "$1($2, $3) Warning: $4" PosHintFormat* = "$1($2, $3) Hint: $4" + PosContextFormat = "$1($2, $3) Info: $4" RawErrorFormat* = "Error: $1" RawWarningFormat* = "Warning: $1" RawHintFormat* = "Hint: $1" @@ -536,10 +537,10 @@ proc writeContext(lastinfo: TLineInfo) = var info = lastInfo for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastInfo and msgContext[i] != info: - MsgWriteln(posErrorFormat % [toFilename(msgContext[i]), - coordToStr(msgContext[i].line), - coordToStr(msgContext[i].col), - getMessageStr(errInstantiationFrom, "")]) + MsgWriteln(posContextFormat % [toFilename(msgContext[i]), + coordToStr(msgContext[i].line), + coordToStr(msgContext[i].col), + getMessageStr(errInstantiationFrom, "")]) info = msgContext[i] proc rawMessage*(msg: TMsgKind, args: openarray[string]) = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cedce3c9c3..7bb6f26e04 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -993,6 +993,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = semDirectOp(c, n, flags) of mSlurp: result = semSlurp(c, n, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags) + of mAstToStr: + if sonsLen(n) == 2: + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) + result.typ = getSysType(tyString) + else: + result = semDirectOp(c, n, flags) else: result = semDirectOp(c, n, flags) proc semIfExpr(c: PContext, n: PNode): PNode = diff --git a/compiler/semfold.nim b/compiler/semfold.nim index bda3e3891f..b2e2f22771 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -209,6 +209,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mParseExprToAst, mParseStmtToAst, mExpandToAst, mNLen..mNError, mEqRef: nil + of mRand: + result = newIntNodeT(math.random(a.getInt.int), n) else: InternalError(a.info, "evalOp(" & $m & ')') proc getConstIfExpr(c: PSym, n: PNode): PNode = @@ -440,6 +442,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = magicCall(m, n) of mIs: result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n) + of mAstToStr: + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) else: result = magicCall(m, n) except EOverflow: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 60c0d5913a..2dbfa94973 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -404,7 +404,7 @@ proc semFor(c: PContext, n: PNode): PNode = openScope(c.tab) n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) var call = n.sons[length-2] - if call.kind != nkCall or call.sons[0].kind != nkSym or + if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or call.sons[0].sym.kind != skIterator: GlobalError(n.sons[length - 2].info, errIteratorExpected) elif call.sons[0].sym.magic != mNone: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 69982bfa5a..cd6cbe8599 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -597,7 +597,7 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate, var f = 1 # iterates over formal parameters var a = 1 # iterates over the actual given arguments m.state = csMatch # until proven otherwise - m.call = newNodeI(nkCall, n.info) + m.call = newNodeI(n.kind, n.info) m.call.typ = base(m.callee) # may be nil var formalLen = sonsLen(m.callee.n) addSon(m.call, copyTree(n.sons[0])) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 9d4658a0b7..4f5f4b9ae2 100755 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -117,7 +117,7 @@ proc getParser(ident: PIdent): TParserKind = rawMessage(errInvalidDirectiveX, ident.s) proc getCallee(n: PNode): PIdent = - if (n.kind == nkCall) and (n.sons[0].kind == nkIdent): + if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: result = n.sons[0].ident elif n.kind == nkIdent: result = n.ident diff --git a/compiler/transf.nim b/compiler/transf.nim index 5f3becaf6b..246536eb79 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -468,7 +468,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = addVar(v, copyTree(n.sons[i])) # declare new vars add(result, v.ptransNode) var call = n.sons[length - 2] - if call.kind != nkCall or call.sons[0].kind != nkSym: + if call.kind notin nkCallKinds or call.sons[0].kind != nkSym: InternalError(call.info, "transformFor") var newC = newTransCon(call.sons[0].sym) diff --git a/lib/system.nim b/lib/system.nim index 145d1552ad..c7e26230a8 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1050,13 +1050,6 @@ proc deallocShared*(p: Pointer) {.noconv, rtl.} ## memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. -proc assert*(cond: bool) {.magic: "Assert", noSideEffect.} - ## provides a means to implement `programming by contracts`:idx: in Nimrod. - ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it - ## raises an ``EAssertionFailure`` exception. However, the compiler may - ## not generate any code at all for ``assert`` if it is advised to do so. - ## Use ``assert`` for debugging purposes only. - proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## swaps the values `a` and `b`. This is often more efficient than ## ``tmp = a; a = b; b = tmp``. Particularly useful for sorting algorithms. @@ -2071,3 +2064,21 @@ proc `/=` *(x: var float, y:float) {.inline, noSideEffect.} = proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} +proc rand*(max: int): int {.magic: "Rand", sideEffect.} + ## compile-time `random` function. Useful for debugging. + +proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} + ## converts the AST of `x` into a string representation. This is very useful + ## for debugging. + +template assert*(cond: expr, msg = "") = + ## provides a means to implement `programming by contracts`:idx: in Nimrod. + ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it + ## raises an ``EAssertionFailure`` exception. However, the compiler may + ## not generate any code at all for ``assert`` if it is advised to do so. + ## Use ``assert`` for debugging purposes only. + when compileOption("assertions"): + if not cond: + raise newException(EAssertionFailed, astToStr(cond) & ' ' & msg) + + diff --git a/lib/system/channels.nim b/lib/system/channels.nim index 4a37bcc14e..fe93d6840e 100755 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -38,7 +38,7 @@ proc initRawChannel(p: pointer) = proc deinitRawChannel(p: pointer) = var c = cast[PRawChannel](p) - # we need to grab the lock to be save against sending threads! + # we need to grab the lock to be safe against sending threads! acquireSys(c.lock) c.mask = ChannelDeadMask deallocOsPages(c.region) @@ -154,7 +154,7 @@ proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) = ## adds an `item` to the end of the queue `q`. var cap = q.mask+1 if q.count >= cap: - # start with capicity for 2 entries in the queue: + # start with capacity for 2 entries in the queue: if cap == 0: cap = 1 var n = cast[pbytes](Alloc0(q.region, cap*2*typ.size)) var z = 0 @@ -175,7 +175,7 @@ proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) = q.wr = (q.wr + 1) and q.mask proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) = - assert q.count > 0 + sysAssert q.count > 0, "rawRecv" dec q.count storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad) q.rd = (q.rd + 1) and q.mask diff --git a/lib/system/ecmasys.nim b/lib/system/ecmasys.nim index 9951811d18..bf8a01ecfb 100755 --- a/lib/system/ecmasys.nim +++ b/lib/system/ecmasys.nim @@ -410,7 +410,7 @@ proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} = case n.kind - of nkNone: sysAssert(false) + of nkNone: sysAssert(false, "NimCopyAux") of nkSlot: asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);" of nkList: diff --git a/tests/run/tuserassert.nim b/tests/run/tuserassert.nim new file mode 100644 index 0000000000..958da2fe18 --- /dev/null +++ b/tests/run/tuserassert.nim @@ -0,0 +1,14 @@ +discard """ + output: "454 == 45ugh" +""" + +template myAssert(cond: expr) = + when rand(3) < 2: + let c = cond.astToStr + {.warning: "code activated: " & c.} + if not cond: + echo c, "ugh" + + +myAssert(454 == 45) + diff --git a/todo.txt b/todo.txt index a2c85224a3..0ff5967c09 100755 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 0.8.14 ============== +- find a proper bugfix for C compilers optimizing away stack roots - warning for implicit openArray -> varargs convention - implement explicit varargs; **but** ``len(varargs)`` problem remains! --> solve by implicit conversion from varargs to openarray @@ -123,7 +124,7 @@ Low priority need to recompile clients; er ... what about templates, macros or anything that has inlining semantics? - codegen should use "NIM_CAST" macro and respect aliasing rules for GCC - +- remove unused mAssert magic Version 2 ========= diff --git a/web/news.txt b/web/news.txt index 9cf410bb56..b4c76cdc39 100755 --- a/web/news.txt +++ b/web/news.txt @@ -53,6 +53,8 @@ Changes affecting backwards compatibility because they should not be used directly anymore. Wrapper procs have been created that should be used instead. - ``export`` is now a keyword. +- ``assert`` is now implemented in pure Nimrod; it's easy to implement your + own assertion schemes now. Language Additions @@ -140,6 +142,7 @@ Library Additions and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to the old implementation. - Added ``intsets.assign``. +- Added ``system.astToStr`` and ``system.rand``. 2011-07-10 Version 0.8.12 released