pragma blocks; fixed line information issue with user defined assertions

This commit is contained in:
Araq 2012-01-17 23:58:18 +01:00
commit 78f4aacde9
17 changed files with 133 additions and 23 deletions

View file

@ -139,6 +139,7 @@ type
nkMacroStmt, # a macro statement nkMacroStmt, # a macro statement
nkAsmStmt, # an assembler block nkAsmStmt, # an assembler block
nkPragma, # a pragma statement nkPragma, # a pragma statement
nkPragmaBlock, # a pragma with a block
nkIfStmt, # an if statement nkIfStmt, # an if statement
nkWhenStmt, # a when statement nkWhenStmt, # a when statement
nkForStmt, # a for statement nkForStmt, # a for statement
@ -390,7 +391,8 @@ type
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mGetTypeInfo mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo
type type
PNode* = ref TNode PNode* = ref TNode

View file

@ -11,11 +11,7 @@
import import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg, ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
platform platform, trees
proc whichPragma*(n: PNode): TSpecialWord =
var key = if n.kind == nkExprColonExpr: n.sons[0] else: n
if key.kind == nkIdent: result = whichKeyword(key.ident)
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind case n.kind

View file

@ -1213,6 +1213,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
of nkMetaNode: of nkMetaNode:
result = copyTree(n.sons[0]) result = copyTree(n.sons[0])
result.typ = n.typ result.typ = n.typ
of nkPragmaBlock:
result = evalAux(c, n.sons[1], flags)
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambda, nkContinueStmt, nkIdent: nkLambda, nkContinueStmt, nkIdent:
result = raiseCannotEval(c, n.info) result = raiseCannotEval(c, n.info)

View file

@ -476,6 +476,12 @@ proc pushInfoContext*(info: TLineInfo) =
proc popInfoContext*() = proc popInfoContext*() =
setlen(msgContext, len(msgContext) - 1) setlen(msgContext, len(msgContext) - 1)
proc getInfoContext*(index: int): TLineInfo =
let L = msgContext.len
let i = if index < 0: L + index else: index
if i >=% L: result = UnknownLineInfo()
else: result = msgContext[i]
proc ToFilename*(info: TLineInfo): string = proc ToFilename*(info: TLineInfo): string =
if info.fileIndex < 0: result = "???" if info.fileIndex < 0: result = "???"
else: result = fileInfos[info.fileIndex].projPath else: result = fileInfos[info.fileIndex].projPath

View file

@ -1316,6 +1316,15 @@ proc parseBind(p: var TParser): PNode =
optInd(p, a) optInd(p, a)
expectNl(p) expectNl(p)
proc parseStmtPragma(p: var TParser): PNode =
result = parsePragma(p)
if p.tok.tokType == tkColon:
let a = result
result = newNodeI(nkPragmaBlock, a.info)
getTok(p)
result.add a
result.add parseStmt(p)
proc simpleStmt(p: var TParser): PNode = proc simpleStmt(p: var TParser): PNode =
case p.tok.tokType case p.tok.tokType
of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt) of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
@ -1324,7 +1333,7 @@ proc simpleStmt(p: var TParser): PNode =
of tkDiscard: result = parseYieldOrDiscard(p, nkDiscardStmt) of tkDiscard: result = parseYieldOrDiscard(p, nkDiscardStmt)
of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt) of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt)
of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt) of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt)
of tkCurlyDotLe: result = parsePragma(p) of tkCurlyDotLe: result = parseStmtPragma(p)
of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt) of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt)
of tkFrom: result = parseFromStmt(p) of tkFrom: result = parseFromStmt(p)
of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt) of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt)

View file

@ -32,6 +32,7 @@ const
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable} wImportcpp, wImportobjc, wError, wDiscardable}
exprPragmas* = {wLine}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@ -394,6 +395,23 @@ proc PragmaUnroll(c: PContext, n: PNode) =
proc PragmaLinearScanEnd(c: PContext, n: PNode) = proc PragmaLinearScanEnd(c: PContext, n: PNode) =
noVal(n) noVal(n)
proc PragmaLine(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr:
n.sons[1] = c.semConstExpr(c, n.sons[1])
let a = n.sons[1]
if a.kind != nkPar: GlobalError(n.info, errXExpected, "tuple")
var x = a.sons[0]
var y = a.sons[1]
if x.kind == nkExprColonExpr: x = x.sons[1]
if y.kind == nkExprColonExpr: y = y.sons[1]
if x.kind != nkStrLit: GlobalError(n.info, errStringLiteralExpected)
if y.kind != nkIntLit: GlobalError(n.info, errIntLiteralExpected)
n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
n.info.line = int16(y.intVal)
else:
# sensible default:
n.info = getInfoContext(-1)
proc processPragma(c: PContext, n: PNode, i: int) = proc processPragma(c: PContext, n: PNode, i: int) =
var it = n.sons[i] var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n) if it.kind != nkExprColonExpr: invalidPragma(n)
@ -575,6 +593,7 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfIncompleteStruct) incl(sym.typ.flags, tfIncompleteStruct)
of wLine: PragmaLine(c, it)
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else: invalidPragma(it)
else: processNote(c, it) else: processNote(c, it)

View file

@ -537,6 +537,16 @@ proc gwhile(g: var TSrcGen, n: PNode) =
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c) gstmts(g, n.sons[1], c)
proc gpragmaBlock(g: var TSrcGen, n: PNode) =
var c: TContext
gsub(g, n.sons[0])
putWithSpace(g, tkColon, ":")
initContext(c)
if longMode(n) or (lsub(n.sons[1]) + g.lineLen > maxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c)
proc gtry(g: var TSrcGen, n: PNode) = proc gtry(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
put(g, tkTry, "try") put(g, tkTry, "try")
@ -933,6 +943,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkWhen, "when") putWithSpace(g, tkWhen, "when")
gif(g, n) gif(g, n)
of nkWhileStmt: gwhile(g, n) of nkWhileStmt: gwhile(g, n)
of nkPragmaBlock: gpragmaBlock(g, n)
of nkCaseStmt, nkRecCase: gcase(g, n) of nkCaseStmt, nkRecCase: gcase(g, n)
of nkMacroStmt: gmacro(g, n) of nkMacroStmt: gmacro(g, n)
of nkTryStmt: gtry(g, n) of nkTryStmt: gtry(g, n)

View file

@ -26,6 +26,23 @@ proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
except EIO: except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal) GlobalError(a.info, errCannotOpenFile, a.strVal)
proc expectIntLit(c: PContext, n: PNode): int =
let x = c.semConstExpr(c, n)
case x.kind
of nkIntLit..nkInt64Lit: result = int(x.intVal)
else: GlobalError(n.info, errIntLiteralExpected)
proc semInstantiationInfo(c: PContext, n: PNode): PNode =
result = newNodeIT(nkPar, n.info, n.typ)
let idx = expectIntLit(c, n.sons[1])
let info = getInfoContext(idx)
var filename = newNodeIT(nkStrLit, n.info, getSysType(tyString))
filename.strVal = ToFilename(info)
var line = newNodeIT(nkIntLit, n.info, getSysType(tyInt))
line.intVal = ToLinenumber(info)
result.add(filename)
result.add(line)
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
case n[0].sym.magic case n[0].sym.magic
@ -34,5 +51,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mAstToStr: of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString) result.typ = getSysType(tyString)
of mInstantiationInfo: result = semInstantiationInfo(c, n)
else: result = n else: result = n

View file

@ -827,6 +827,18 @@ proc evalInclude(c: PContext, n: PNode): PNode =
addSon(result, semStmt(c, gIncludeFile(f))) addSon(result, semStmt(c, gIncludeFile(f)))
Excl(c.includedFiles, fileIndex) Excl(c.includedFiles, fileIndex)
proc setLine(n: PNode, info: TLineInfo) =
for i in 0 .. <safeLen(n): setLine(n.sons[i], info)
n.info = info
proc semPragmaBlock(c: PContext, n: PNode): PNode =
let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas)
result = semStmt(c, n.sons[1])
for i in 0 .. <pragmaList.len:
if whichPragma(pragmaList.sons[i]) == wLine:
setLine(result, pragmaList.sons[i].info)
proc SemStmt(c: PContext, n: PNode): PNode = proc SemStmt(c: PContext, n: PNode): PNode =
const # must be last statements in a block: const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@ -881,6 +893,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
of nkIncludeStmt: of nkIncludeStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include") if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n) result = evalInclude(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
else: else:
# in interactive mode, we embed the expression in an 'echo': # in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive: if gCmd == cmdInteractive:

View file

@ -146,3 +146,7 @@ proc IsRange*(n: PNode): bool {.inline.} =
n[0].kind == nkSymChoice and n[0][1].sym.name.id == ord(wDotDot): n[0].kind == nkSymChoice and n[0][1].sym.name.id == ord(wDotDot):
result = true result = true
proc whichPragma*(n: PNode): TSpecialWord =
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
if key.kind == nkIdent: result = whichKeyword(key.ident)

View file

@ -1,5 +1,5 @@
# Configuration file for the Nimrod Compiler. # Configuration file for the Nimrod Compiler.
# (c) 2011 Andreas Rumpf # (c) 2012 Andreas Rumpf
# Feel free to edit the default values as you need. # Feel free to edit the default values as you need.
@ -77,6 +77,7 @@ icc.options.linker = "-cxxlib"
gcc.options.debug = "-g3 -O0" gcc.options.debug = "-g3 -O0"
@if macosx: @if macosx:
tlsEmulation:on
@if not release: @if not release:
gcc.options.always = "-w -fasm-blocks -O1" gcc.options.always = "-w -fasm-blocks -O1"
@else: @else:

View file

@ -35,7 +35,6 @@ primarySuffix ::= '.' optInd symbol [generalizedLit]
| '(' optInd namedExprList optPar ')' | '(' optInd namedExprList optPar ')'
| '[' optInd [indexExpr (comma indexExpr)* [comma]] optPar ']' | '[' optInd [indexExpr (comma indexExpr)* [comma]] optPar ']'
| '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}' | '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}'
| pragma
primary ::= primaryPrefix* (symbol [generalizedLit] | primary ::= primaryPrefix* (symbol [generalizedLit] |
constructor | castExpr | addrExpr) constructor | castExpr | addrExpr)
@ -84,13 +83,15 @@ macroStmt ::= ':' [stmt] ('of' [exprList] ':' stmt
|'except' exceptList ':' stmt )* |'except' exceptList ':' stmt )*
['else' ':' stmt] ['else' ':' stmt]
pragmaBlock ::= pragma [':' stmt]
simpleStmt ::= returnStmt simpleStmt ::= returnStmt
| yieldStmt | yieldStmt
| discardStmt | discardStmt
| raiseStmt | raiseStmt
| breakStmt | breakStmt
| continueStmt | continueStmt
| pragma | pragmaBlock
| importStmt | importStmt
| fromStmt | fromStmt
| includeStmt | includeStmt

View file

@ -2570,10 +2570,11 @@ occuring in a generic:
echo a == b # works! echo a == b # works!
In the example the generic ``==`` for tuples uses the ``==`` operators of the In the example the generic ``==`` for tuples (as defined in the system module)
tuple's components. However, the ``==`` for the ``TIndex`` type is uses the ``==`` operators of the tuple's components. However, the ``==`` for
defined *after* the ``==`` for tuples; yet the example compiles as the the ``TIndex`` type is defined *after* the ``==`` for tuples; yet the example
instantiation takes the currently defined symbols into account too. compiles as the instantiation takes the currently defined symbols into account
too.
Templates Templates
@ -2679,7 +2680,8 @@ Syntax::
bindStmt ::= 'bind' IDENT (comma IDENT)* bindStmt ::= 'bind' IDENT (comma IDENT)*
Exporting a template is a often a leaky abstraction. However, to compensate for Exporting a template is a often a leaky abstraction as it can depend on
symbols that are not visible from a client module. However, to compensate for
this case, a `bind`:idx: statement can be used: It declares all identifiers this case, a `bind`:idx: statement can be used: It declares all identifiers
that should be bound early (i.e. when the template is parsed): that should be bound early (i.e. when the template is parsed):
@ -3096,6 +3098,23 @@ hint pragma
The `hint`:idx: pragma is used to make the compiler output a hint message with The `hint`:idx: pragma is used to make the compiler output a hint message with
the given content. Compilation continues after the hint. the given content. Compilation continues after the hint.
line pragma
-----------
The `line`:idx: pragma can be used to affect line information of the annotated
statement as seen in stack backtraces:
.. code-bock:: nimrod
template myassert*(cond: expr, msg = "") =
if not cond:
# change run-time line information of the 'raise' statement:
{.line: InstantiationInfo().}:
raise newException(EAssertionFailed, msg)
If the ``line`` pragma is used with a parameter, the parameter needs be a
``tuple[filename: string, line: int]``. If it is used without a parameter,
``system.InstantiationInfo()`` is used.
linearScanEnd pragma linearScanEnd pragma
-------------------- --------------------
@ -3315,6 +3334,7 @@ Dynlib pragma for import
With the `dynlib`:idx: pragma a procedure can be imported from With the `dynlib`:idx: pragma a procedure can be imported from
a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The
non-optional argument has to be the name of the dynamic library: non-optional argument has to be the name of the dynamic library:
.. code-block:: Nimrod .. code-block:: Nimrod

View file

@ -35,7 +35,7 @@ type
nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef, nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt,
nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
nnkConstDef, nnkTypeDef, nnkConstDef, nnkTypeDef,

View file

@ -2080,6 +2080,12 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful ## converts the AST of `x` into a string representation. This is very useful
## for debugging. ## for debugging.
proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
magic: "InstantiationInfo", noSideEffect.}
## provides access to the compiler's instantiation stack line information.
## This is only useful for advanced meta programming. See the implementation
## of `assert` for an example.
proc raiseAssert(msg: string) {.noinline.} = proc raiseAssert(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg) raise newException(EAssertionFailed, msg)
@ -2089,15 +2095,17 @@ template assert*(cond: expr, msg = "") =
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so. ## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only. ## Use ``assert`` for debugging purposes only.
bind raiseAssert bind raiseAssert, InstantiationInfo
when compileOption("assertions"): when compileOption("assertions"):
if not cond: if not cond:
raiseAssert(astToStr(cond) & ' ' & msg) {.line.}:
raiseAssert(astToStr(cond) & ' ' & msg)
template doAssert*(cond: expr, msg = "") = template doAssert*(cond: expr, msg = "") =
## same as `assert' but is always turned on and not affected by the ## same as `assert` but is always turned on and not affected by the
## ``--assertions`` command line switch. ## ``--assertions`` command line switch.
bind raiseAssert bind raiseAssert, InstantiationInfo
if not cond: if not cond:
raiseAssert(astToStr(cond) & ' ' & msg) {.line: InstantiationInfo().}:
raiseAssert(astToStr(cond) & ' ' & msg)

View file

@ -1,7 +1,7 @@
# To test stack overflow message # To test stack overflow message
proc over(a: int): int = proc over(a: int): int =
if a >= 400: if a >= 10:
assert false assert false
return return
result = over(a+1)+5 result = over(a+1)+5

View file

@ -1,7 +1,6 @@
version 0.8.14 version 0.8.14
============== ==============
- fix line info in assertions
- bug: tsortdev does not run with native GC - bug: tsortdev does not run with native GC
version 0.9.0 version 0.9.0