summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim4
-rwxr-xr-xcompiler/ccgutils.nim6
-rwxr-xr-xcompiler/evals.nim2
-rwxr-xr-xcompiler/msgs.nim6
-rwxr-xr-xcompiler/parser.nim11
-rwxr-xr-xcompiler/pragmas.nim19
-rwxr-xr-xcompiler/renderer.nim11
-rw-r--r--compiler/semmagic.nim18
-rwxr-xr-xcompiler/semstmts.nim14
-rwxr-xr-xcompiler/trees.nim4
-rwxr-xr-xconfig/nimrod.cfg3
-rwxr-xr-xdoc/grammar.txt5
-rwxr-xr-xdoc/manual.txt30
-rwxr-xr-xlib/core/macros.nim2
-rwxr-xr-xlib/system.nim18
-rwxr-xr-xtests/stckovfl.nim2
-rwxr-xr-xtodo.txt1
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