mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-07 20:34:21 +00:00
pragma blocks; fixed line information issue with user defined assertions
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user