diff options
-rwxr-xr-x | compiler/ast.nim | 4 | ||||
-rwxr-xr-x | compiler/ccgutils.nim | 6 | ||||
-rwxr-xr-x | compiler/evals.nim | 2 | ||||
-rwxr-xr-x | compiler/msgs.nim | 6 | ||||
-rwxr-xr-x | compiler/parser.nim | 11 | ||||
-rwxr-xr-x | compiler/pragmas.nim | 19 | ||||
-rwxr-xr-x | compiler/renderer.nim | 11 | ||||
-rw-r--r-- | compiler/semmagic.nim | 18 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 14 | ||||
-rwxr-xr-x | compiler/trees.nim | 4 | ||||
-rwxr-xr-x | config/nimrod.cfg | 3 | ||||
-rwxr-xr-x | doc/grammar.txt | 5 | ||||
-rwxr-xr-x | doc/manual.txt | 30 | ||||
-rwxr-xr-x | lib/core/macros.nim | 2 | ||||
-rwxr-xr-x | lib/system.nim | 18 | ||||
-rwxr-xr-x | tests/stckovfl.nim | 2 | ||||
-rwxr-xr-x | todo.txt | 1 |
17 files changed, 133 insertions, 23 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index db5d8b9f0..76260b586 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 578512bb0..610fcb39b 100755 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -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 diff --git a/compiler/evals.nim b/compiler/evals.nim index d4602aa24..311b01b84 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -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) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6cb2fedcd..e1deb6e35 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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 diff --git a/compiler/parser.nim b/compiler/parser.nim index 118b96736..12184a097 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 762bfa5bf..4c38dcb1a 100755 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 04cff007b..d26896b8e 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -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) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 6b868d19b..932f36c2f 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6208f6bc9..54625b46e 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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: diff --git a/compiler/trees.nim b/compiler/trees.nim index 3b35b2ff6..f393bfc66 100755 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -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) + diff --git a/config/nimrod.cfg b/config/nimrod.cfg index b8bb77a03..d81295e66 100755 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -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: diff --git a/doc/grammar.txt b/doc/grammar.txt index 5f7b851fa..325a29ad5 100755 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -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 diff --git a/doc/manual.txt b/doc/manual.txt index bbc07c09b..abdcc05d5 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 19c25dc36..a8eb0daaf 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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, diff --git a/lib/system.nim b/lib/system.nim index 334904056..92c907618 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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) diff --git a/tests/stckovfl.nim b/tests/stckovfl.nim index 205d73edf..eeb499bed 100755 --- a/tests/stckovfl.nim +++ b/tests/stckovfl.nim @@ -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 diff --git a/todo.txt b/todo.txt index a8efefcd4..61d5b5c0c 100755 --- a/todo.txt +++ b/todo.txt @@ -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 |