'assert' is now implemented without compiler magic

This commit is contained in:
Araq
2011-12-04 20:14:50 +01:00
parent 728328eec2
commit 70cf34cbdc
16 changed files with 66 additions and 24 deletions

View File

@@ -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,

View File

@@ -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)

View File

@@ -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 :-(

View File

@@ -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]) =

View File

@@ -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 =

View File

@@ -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:

View File

@@ -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:

View File

@@ -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]))

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

14
tests/run/tuserassert.nim Normal file
View 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)

View File

@@ -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
=========

View File

@@ -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