diff --git a/lib/std/system.nim b/lib/std/system.nim index 70c52ca8..b3039bde 100644 --- a/lib/std/system.nim +++ b/lib/std/system.nim @@ -371,3 +371,39 @@ proc addr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} + +proc instantiationInfo*(index = -1, fullPaths = false): tuple[ + filename: string, line: int, column: int] {.magic: "InstantiationInfo", noSideEffect.} + ## Provides access to the compiler's instantiation stack line information + ## of a template. + ## + ## While similar to the `caller info`:idx: of other languages, it is determined + ## at compile time. + ## + ## This proc is mostly useful for meta programming (eg. `assert` template) + ## to retrieve information about the current filename and line number. + ## Example: + ## + ## ```nim + ## import std/strutils + ## + ## template testException(exception, code: untyped): typed = + ## try: + ## let pos = instantiationInfo() + ## discard(code) + ## echo "Test failure at $1:$2 with '$3'" % [pos.filename, + ## $pos.line, astToStr(code)] + ## assert false, "A test expecting failure succeeded?" + ## except exception: + ## discard + ## + ## proc tester(pos: int): int = + ## let + ## a = @[1, 2, 3] + ## result = a[pos] + ## + ## when isMainModule: + ## testException(IndexDefect, tester(30)) + ## testException(IndexDefect, tester(1)) + ## # --> Test failure at example.nim:20 with 'tester(1)' + ## ``` diff --git a/src/hexer/duplifier.nim b/src/hexer/duplifier.nim index 5152a3d2..7bbe0221 100644 --- a/src/hexer/duplifier.nim +++ b/src/hexer/duplifier.nim @@ -588,7 +588,7 @@ proc tr(c: var Context; n: var Cursor; e: Expects) = DefinedX, HighX, LowX, TypeofX, UnpackX, EnumToStrX, IsMainModuleX, QuotedX, DerefX, HderefX, AddrX, HaddrX: trSons c, n, WantNonOwner - of DefaultObjX, DefaultTupX: + of DefaultObjX, DefaultTupX, InstantiationInfoX: raiseAssert "nodekind should have been eliminated in sem.nim" of NoExpr: case n.stmtKind diff --git a/src/nimony/magics.nim b/src/nimony/magics.nim index addf969b..ffce164a 100644 --- a/src/nimony/magics.nim +++ b/src/nimony/magics.nim @@ -174,6 +174,7 @@ proc magicToTag*(m: TMagic): (string, int) = of mDefaultTup: res DefaultTupX of mOpenArray: res OpenArrayT of mEnsureMove: res EnsureMoveX + of mInstantiationInfo: res InstantiationInfoX else: ("", 0) when isMainModule: diff --git a/src/nimony/nimony_model.nim b/src/nimony/nimony_model.nim index b56c52f9..e49892d4 100644 --- a/src/nimony/nimony_model.nim +++ b/src/nimony/nimony_model.nim @@ -153,6 +153,7 @@ type ArrAtX = "arrat" TupAtX = "tupat" # tup[0] syntax EnsureMoveX = "emove" # note that `move` can be written in standard Nim + InstantiationInfoX = "instantiationinfo" TypeKind* = enum NoType diff --git a/src/nimony/sem.nim b/src/nimony/sem.nim index f4eca06e..db88e87c 100644 --- a/src/nimony/sem.nim +++ b/src/nimony/sem.nim @@ -8,7 +8,7 @@ ## Most important task is to turn identifiers into symbols and to perform ## type checking. -import std / [tables, sets, syncio, formatfloat, assertions, packedsets, strutils] +import std / [os, tables, sets, syncio, formatfloat, assertions, packedsets, strutils] include nifprelude import nimony_model, symtabs, builtintypes, decls, symparser, asthelpers, programs, sigmatch, magics, reporters, nifconfig, nifindexes, @@ -787,7 +787,7 @@ proc addFn(c: var SemContext; fn: FnCandidate; fnOrig: Cursor; args: openArray[I if n.kind == ParLe: if n.exprKind in {DefinedX, DeclaredX, CompilesX, TypeofX, LowX, HighX, AddrX, EnumToStrX, DefaultObjX, DefaultTupX, - ArrAtX, DerefX, TupAtX}: + ArrAtX, DerefX, TupAtX, InstantiationInfoX}: # magic needs semchecking after overloading result = MagicCallNeedsSemcheck else: @@ -3930,6 +3930,48 @@ proc semTupAt(c: var SemContext; it: var Item) = wantParRi c, it.n commonType c, it, exprStart, expected +proc semInstantiationInfo(c: var SemContext, it: var Item) = + let info = it.n.info + let expected = it.typ + let exprStart = c.dest.len + + c.dest.addParLe(TupleConstrX, info) + inc it.n + + # TODO: info stack + let idx: xint = evalOrdinal(c, it.n) + if idx.isNaN: + c.buildErr it.n.info, "cannot evaluate at compile time: " & toString(it.n) + skip it.n + let useFullPaths = evalOrdinal(c, it.n) + if useFullPaths.isNaN: + c.buildErr it.n.info, "cannot evaluate at compile time: " & toString(it.n) + skip it.n + + let (file, line, col) = unpack(pool.man, info) + + let filename: string + if useFullPaths == createXint(1'i64): + filename = absolutePath(pool.files[file]) + else: + filename = pool.files[file] + c.dest.addStrLit filename, info + c.dest.addIntLit line, info + c.dest.addIntLit col, info + + wantParRi c, it.n + + let typeStart = c.dest.len + + c.dest.buildTree TupleT, info: + c.dest.addSubtree c.types.stringType + c.dest.addSubtree c.types.intType + c.dest.addSubtree c.types.intType + + it.typ = typeToCursor(c, typeStart) + c.dest.shrink typeStart + commonType c, it, exprStart, expected + proc getDottedIdent(n: var Cursor): string = let isError = n.kind == ParLe and n.tagId == ErrT if isError: @@ -4738,6 +4780,8 @@ proc semExpr(c: var SemContext; it: var Item; flags: set[SemFlag] = {}) = semObjDefault c, it of DefaultTupX: semTupleDefault c, it + of InstantiationInfoX: + semInstantiationInfo c, it of LowX: semLow c, it of HighX: diff --git a/src/nimony/typenav.nim b/src/nimony/typenav.nim index 243c0c39..449b820f 100644 --- a/src/nimony/typenav.nim +++ b/src/nimony/typenav.nim @@ -142,7 +142,7 @@ proc getTypeImpl(c: var TypeCache; n: Cursor): Cursor = result = c.builtins.boolType of NegX, NegInfX, NanX, InfX: result = c.builtins.floatType - of EnumToStrX, DefaultObjX, DefaultTupX: + of EnumToStrX, DefaultObjX, DefaultTupX, InstantiationInfoX: result = c.builtins.stringType of SizeofX: result = c.builtins.intType diff --git a/tests/nimony/sysbasics/tbasics.nif b/tests/nimony/sysbasics/tbasics.nif index 7033ee39..fc7bef7d 100644 --- a/tests/nimony/sysbasics/tbasics.nif +++ b/tests/nimony/sysbasics/tbasics.nif @@ -114,4 +114,10 @@ (pointer) 5 (cast 5 (pointer) 14 m.3)) 4,4 - (call ~4 foo3.0.tbawx6nu81 1 m2.0)))) \ No newline at end of file + (call ~4 foo3.0.tbawx6nu81 1 m2.0))) 4,35 + (let :sInfo.0.tbawx6nu81 . . 25 + (tuple + (string) + (i -1) + (i -1)) 25 + (tup "tests/nimony/sysbasics/tbasics.nim" +36 +29))) \ No newline at end of file diff --git a/tests/nimony/sysbasics/tbasics.nim b/tests/nimony/sysbasics/tbasics.nim index b1493532..7823dee7 100644 --- a/tests/nimony/sysbasics/tbasics.nim +++ b/tests/nimony/sysbasics/tbasics.nim @@ -31,3 +31,6 @@ block: foo3(m) let m2 = cast[pointer](m) foo3(m2) + + +let sInfo = instantiationInfo(-1, false)