'assert' is now implemented without compiler magic
This commit is contained in:
parent
728328eec2
commit
70cf34cbdc
16 changed files with 66 additions and 24 deletions
|
|
@ -370,7 +370,8 @@ type
|
||||||
mConTArr, mConTT, mSlice,
|
mConTArr, mConTT, mSlice,
|
||||||
mFields, mFieldPairs,
|
mFields, mFieldPairs,
|
||||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||||
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
|
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
||||||
|
mAssert, mAstToStr, mRand,
|
||||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||||
mNewString, mNewStringOfCap,
|
mNewString, mNewStringOfCap,
|
||||||
mReset,
|
mReset,
|
||||||
|
|
|
||||||
|
|
@ -607,7 +607,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||||
InternalError(e.info, "genCheckedRecordField") # generate the checks:
|
InternalError(e.info, "genCheckedRecordField") # generate the checks:
|
||||||
for i in countup(1, sonsLen(e) - 1):
|
for i in countup(1, sonsLen(e) - 1):
|
||||||
it = e.sons[i]
|
it = e.sons[i]
|
||||||
assert(it.kind == nkCall)
|
assert(it.kind in nkCallKinds)
|
||||||
assert(it.sons[0].kind == nkSym)
|
assert(it.sons[0].kind == nkSym)
|
||||||
op = it.sons[0].sym
|
op = it.sons[0].sym
|
||||||
if op.magic == mNot: it = it.sons[1]
|
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,
|
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
|
||||||
mInSet:
|
mInSet:
|
||||||
genSetOp(p, e, d, op)
|
genSetOp(p, e, d, op)
|
||||||
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit:
|
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mRand:
|
||||||
var opr = e.sons[0].sym
|
var opr = e.sons[0].sym
|
||||||
if lfNoDecl notin opr.loc.flags:
|
if lfNoDecl notin opr.loc.flags:
|
||||||
discard cgsym(p.module, opr.loc.r.ropeToStr)
|
discard cgsym(p.module, opr.loc.r.ropeToStr)
|
||||||
|
|
|
||||||
|
|
@ -1162,7 +1162,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
of nkEmpty: result = n
|
of nkEmpty: result = n
|
||||||
of nkSym: result = evalSym(c, n, flags)
|
of nkSym: result = evalSym(c, n, flags)
|
||||||
of nkType..nkNilLit: result = copyNode(n) # end of atoms
|
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)
|
result = evalMagicOrCall(c, n)
|
||||||
of nkCurly, nkBracket, nkRange:
|
of nkCurly, nkBracket, nkRange:
|
||||||
# flags need to be passed here for mNAddMultiple :-(
|
# flags need to be passed here for mNAddMultiple :-(
|
||||||
|
|
|
||||||
|
|
@ -412,6 +412,7 @@ const
|
||||||
PosErrorFormat* = "$1($2, $3) Error: $4"
|
PosErrorFormat* = "$1($2, $3) Error: $4"
|
||||||
PosWarningFormat* = "$1($2, $3) Warning: $4"
|
PosWarningFormat* = "$1($2, $3) Warning: $4"
|
||||||
PosHintFormat* = "$1($2, $3) Hint: $4"
|
PosHintFormat* = "$1($2, $3) Hint: $4"
|
||||||
|
PosContextFormat = "$1($2, $3) Info: $4"
|
||||||
RawErrorFormat* = "Error: $1"
|
RawErrorFormat* = "Error: $1"
|
||||||
RawWarningFormat* = "Warning: $1"
|
RawWarningFormat* = "Warning: $1"
|
||||||
RawHintFormat* = "Hint: $1"
|
RawHintFormat* = "Hint: $1"
|
||||||
|
|
@ -536,10 +537,10 @@ proc writeContext(lastinfo: TLineInfo) =
|
||||||
var info = lastInfo
|
var info = lastInfo
|
||||||
for i in countup(0, len(msgContext) - 1):
|
for i in countup(0, len(msgContext) - 1):
|
||||||
if msgContext[i] != lastInfo and msgContext[i] != info:
|
if msgContext[i] != lastInfo and msgContext[i] != info:
|
||||||
MsgWriteln(posErrorFormat % [toFilename(msgContext[i]),
|
MsgWriteln(posContextFormat % [toFilename(msgContext[i]),
|
||||||
coordToStr(msgContext[i].line),
|
coordToStr(msgContext[i].line),
|
||||||
coordToStr(msgContext[i].col),
|
coordToStr(msgContext[i].col),
|
||||||
getMessageStr(errInstantiationFrom, "")])
|
getMessageStr(errInstantiationFrom, "")])
|
||||||
info = msgContext[i]
|
info = msgContext[i]
|
||||||
|
|
||||||
proc rawMessage*(msg: TMsgKind, args: openarray[string]) =
|
proc rawMessage*(msg: TMsgKind, args: openarray[string]) =
|
||||||
|
|
|
||||||
|
|
@ -993,6 +993,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
of mSlurp: result = semSlurp(c, n, flags)
|
of mSlurp: result = semSlurp(c, n, flags)
|
||||||
of mExpandToAst: result = semExpandToAst(c, n, s, 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)
|
else: result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
proc semIfExpr(c: PContext, n: PNode): PNode =
|
proc semIfExpr(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst,
|
||||||
mNLen..mNError, mEqRef:
|
mNLen..mNError, mEqRef:
|
||||||
nil
|
nil
|
||||||
|
of mRand:
|
||||||
|
result = newIntNodeT(math.random(a.getInt.int), n)
|
||||||
else: InternalError(a.info, "evalOp(" & $m & ')')
|
else: InternalError(a.info, "evalOp(" & $m & ')')
|
||||||
|
|
||||||
proc getConstIfExpr(c: PSym, n: PNode): PNode =
|
proc getConstIfExpr(c: PSym, n: PNode): PNode =
|
||||||
|
|
@ -440,6 +442,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
result = magicCall(m, n)
|
result = magicCall(m, n)
|
||||||
of mIs:
|
of mIs:
|
||||||
result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n)
|
result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n)
|
||||||
|
of mAstToStr:
|
||||||
|
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
|
||||||
else:
|
else:
|
||||||
result = magicCall(m, n)
|
result = magicCall(m, n)
|
||||||
except EOverflow:
|
except EOverflow:
|
||||||
|
|
|
||||||
|
|
@ -404,7 +404,7 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
openScope(c.tab)
|
openScope(c.tab)
|
||||||
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
||||||
var call = n.sons[length-2]
|
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:
|
call.sons[0].sym.kind != skIterator:
|
||||||
GlobalError(n.sons[length - 2].info, errIteratorExpected)
|
GlobalError(n.sons[length - 2].info, errIteratorExpected)
|
||||||
elif call.sons[0].sym.magic != mNone:
|
elif call.sons[0].sym.magic != mNone:
|
||||||
|
|
|
||||||
|
|
@ -597,7 +597,7 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
|
||||||
var f = 1 # iterates over formal parameters
|
var f = 1 # iterates over formal parameters
|
||||||
var a = 1 # iterates over the actual given arguments
|
var a = 1 # iterates over the actual given arguments
|
||||||
m.state = csMatch # until proven otherwise
|
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
|
m.call.typ = base(m.callee) # may be nil
|
||||||
var formalLen = sonsLen(m.callee.n)
|
var formalLen = sonsLen(m.callee.n)
|
||||||
addSon(m.call, copyTree(n.sons[0]))
|
addSon(m.call, copyTree(n.sons[0]))
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ proc getParser(ident: PIdent): TParserKind =
|
||||||
rawMessage(errInvalidDirectiveX, ident.s)
|
rawMessage(errInvalidDirectiveX, ident.s)
|
||||||
|
|
||||||
proc getCallee(n: PNode): PIdent =
|
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
|
result = n.sons[0].ident
|
||||||
elif n.kind == nkIdent:
|
elif n.kind == nkIdent:
|
||||||
result = n.ident
|
result = n.ident
|
||||||
|
|
|
||||||
|
|
@ -468,7 +468,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
addVar(v, copyTree(n.sons[i])) # declare new vars
|
addVar(v, copyTree(n.sons[i])) # declare new vars
|
||||||
add(result, v.ptransNode)
|
add(result, v.ptransNode)
|
||||||
var call = n.sons[length - 2]
|
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")
|
InternalError(call.info, "transformFor")
|
||||||
|
|
||||||
var newC = newTransCon(call.sons[0].sym)
|
var newC = newTransCon(call.sons[0].sym)
|
||||||
|
|
|
||||||
|
|
@ -1050,13 +1050,6 @@ proc deallocShared*(p: Pointer) {.noconv, rtl.}
|
||||||
## memory (or just freeing it twice!) a core dump may happen
|
## memory (or just freeing it twice!) a core dump may happen
|
||||||
## or other memory may be corrupted.
|
## 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.}
|
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
||||||
## swaps the values `a` and `b`. This is often more efficient than
|
## swaps the values `a` and `b`. This is often more efficient than
|
||||||
## ``tmp = a; a = b; b = tmp``. Particularly useful for sorting algorithms.
|
## ``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 `&=`* (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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ proc initRawChannel(p: pointer) =
|
||||||
|
|
||||||
proc deinitRawChannel(p: pointer) =
|
proc deinitRawChannel(p: pointer) =
|
||||||
var c = cast[PRawChannel](p)
|
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)
|
acquireSys(c.lock)
|
||||||
c.mask = ChannelDeadMask
|
c.mask = ChannelDeadMask
|
||||||
deallocOsPages(c.region)
|
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`.
|
## adds an `item` to the end of the queue `q`.
|
||||||
var cap = q.mask+1
|
var cap = q.mask+1
|
||||||
if q.count >= cap:
|
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
|
if cap == 0: cap = 1
|
||||||
var n = cast[pbytes](Alloc0(q.region, cap*2*typ.size))
|
var n = cast[pbytes](Alloc0(q.region, cap*2*typ.size))
|
||||||
var z = 0
|
var z = 0
|
||||||
|
|
@ -175,7 +175,7 @@ proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||||
q.wr = (q.wr + 1) and q.mask
|
q.wr = (q.wr + 1) and q.mask
|
||||||
|
|
||||||
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
|
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||||
assert q.count > 0
|
sysAssert q.count > 0, "rawRecv"
|
||||||
dec q.count
|
dec q.count
|
||||||
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
|
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
|
||||||
q.rd = (q.rd + 1) and q.mask
|
q.rd = (q.rd + 1) and q.mask
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,7 @@ proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
||||||
|
|
||||||
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} =
|
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkNone: sysAssert(false)
|
of nkNone: sysAssert(false, "NimCopyAux")
|
||||||
of nkSlot:
|
of nkSlot:
|
||||||
asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);"
|
asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);"
|
||||||
of nkList:
|
of nkList:
|
||||||
|
|
|
||||||
14
tests/run/tuserassert.nim
Normal file
14
tests/run/tuserassert.nim
Normal file
|
|
@ -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)
|
||||||
|
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -1,6 +1,7 @@
|
||||||
version 0.8.14
|
version 0.8.14
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
- find a proper bugfix for C compilers optimizing away stack roots
|
||||||
- warning for implicit openArray -> varargs convention
|
- warning for implicit openArray -> varargs convention
|
||||||
- implement explicit varargs; **but** ``len(varargs)`` problem remains!
|
- implement explicit varargs; **but** ``len(varargs)`` problem remains!
|
||||||
--> solve by implicit conversion from varargs to openarray
|
--> 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
|
need to recompile clients; er ... what about templates, macros or anything
|
||||||
that has inlining semantics?
|
that has inlining semantics?
|
||||||
- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC
|
- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC
|
||||||
|
- remove unused mAssert magic
|
||||||
|
|
||||||
Version 2
|
Version 2
|
||||||
=========
|
=========
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ Changes affecting backwards compatibility
|
||||||
because they should not be used directly anymore.
|
because they should not be used directly anymore.
|
||||||
Wrapper procs have been created that should be used instead.
|
Wrapper procs have been created that should be used instead.
|
||||||
- ``export`` is now a keyword.
|
- ``export`` is now a keyword.
|
||||||
|
- ``assert`` is now implemented in pure Nimrod; it's easy to implement your
|
||||||
|
own assertion schemes now.
|
||||||
|
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
|
|
@ -140,6 +142,7 @@ Library Additions
|
||||||
and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
|
and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
|
||||||
the old implementation.
|
the old implementation.
|
||||||
- Added ``intsets.assign``.
|
- Added ``intsets.assign``.
|
||||||
|
- Added ``system.astToStr`` and ``system.rand``.
|
||||||
|
|
||||||
|
|
||||||
2011-07-10 Version 0.8.12 released
|
2011-07-10 Version 0.8.12 released
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue