pragma blocks; fixed line information issue with user defined assertions

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

View File

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

View File

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

View File

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

View File

@@ -476,6 +476,12 @@ proc pushInfoContext*(info: TLineInfo) =
proc popInfoContext*() =
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 =
if info.fileIndex < 0: result = "???"
else: result = fileInfos[info.fileIndex].projPath

View File

@@ -1316,6 +1316,15 @@ proc parseBind(p: var TParser): PNode =
optInd(p, a)
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 =
case p.tok.tokType
of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
@@ -1324,7 +1333,7 @@ proc simpleStmt(p: var TParser): PNode =
of tkDiscard: result = parseYieldOrDiscard(p, nkDiscardStmt)
of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt)
of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt)
of tkCurlyDotLe: result = parsePragma(p)
of tkCurlyDotLe: result = parseStmtPragma(p)
of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt)
of tkFrom: result = parseFromStmt(p)
of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt)

View File

@@ -32,6 +32,7 @@ const
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable}
exprPragmas* = {wLine}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@@ -394,6 +395,23 @@ proc PragmaUnroll(c: PContext, n: PNode) =
proc PragmaLinearScanEnd(c: PContext, n: PNode) =
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) =
var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n)
@@ -575,6 +593,7 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfIncompleteStruct)
of wLine: PragmaLine(c, it)
else: invalidPragma(it)
else: invalidPragma(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
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) =
var c: TContext
put(g, tkTry, "try")
@@ -933,6 +943,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkWhen, "when")
gif(g, n)
of nkWhileStmt: gwhile(g, n)
of nkPragmaBlock: gpragmaBlock(g, n)
of nkCaseStmt, nkRecCase: gcase(g, n)
of nkMacroStmt: gmacro(g, n)
of nkTryStmt: gtry(g, n)

View File

@@ -26,6 +26,23 @@ proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
except EIO:
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,
flags: TExprFlags): PNode =
case n[0].sym.magic
@@ -34,5 +51,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
of mInstantiationInfo: result = semInstantiationInfo(c, n)
else: result = n

View File

@@ -827,6 +827,18 @@ proc evalInclude(c: PContext, n: PNode): PNode =
addSon(result, semStmt(c, gIncludeFile(f)))
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 =
const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@@ -881,6 +893,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
of nkIncludeStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
else:
# in interactive mode, we embed the expression in an 'echo':
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):
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.
# (c) 2011 Andreas Rumpf
# (c) 2012 Andreas Rumpf
# Feel free to edit the default values as you need.
@@ -77,6 +77,7 @@ icc.options.linker = "-cxxlib"
gcc.options.debug = "-g3 -O0"
@if macosx:
tlsEmulation:on
@if not release:
gcc.options.always = "-w -fasm-blocks -O1"
@else:

View File

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

View File

@@ -2570,10 +2570,11 @@ occuring in a generic:
echo a == b # works!
In the example the generic ``==`` for tuples uses the ``==`` operators of the
tuple's components. However, the ``==`` for the ``TIndex`` type is
defined *after* the ``==`` for tuples; yet the example compiles as the
instantiation takes the currently defined symbols into account too.
In the example the generic ``==`` for tuples (as defined in the system module)
uses the ``==`` operators of the tuple's components. However, the ``==`` for
the ``TIndex`` type is defined *after* the ``==`` for tuples; yet the example
compiles as the instantiation takes the currently defined symbols into account
too.
Templates
@@ -2679,7 +2680,8 @@ Syntax::
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
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 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
--------------------
@@ -3315,6 +3334,7 @@ Dynlib pragma for import
With the `dynlib`:idx: pragma a procedure can be imported from
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:
.. code-block:: Nimrod

View File

@@ -35,7 +35,7 @@ type
nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt,
nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
nnkForStmt, nnkWhileStmt, nnkCaseStmt,
nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
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
## 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.} =
raise newException(EAssertionFailed, msg)
@@ -2089,15 +2095,17 @@ template assert*(cond: expr, msg = "") =
## 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.
bind raiseAssert
bind raiseAssert, InstantiationInfo
when compileOption("assertions"):
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)
{.line.}:
raiseAssert(astToStr(cond) & ' ' & 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.
bind raiseAssert
bind raiseAssert, InstantiationInfo
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)
{.line: InstantiationInfo().}:
raiseAssert(astToStr(cond) & ' ' & msg)

View File

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

View File

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