diff --git a/compiler/sem.nim b/compiler/sem.nim index 3392db7a9da02..423d8846a22e6 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -850,6 +850,11 @@ proc recoverContext(c: PContext) = while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next proc semWithPContext*(c: PContext, n: PNode): PNode = + var n = n + if sfMainModule in c.module.flags: + n = newTree(nkStmtList, n, + newTree(nkCall, + newIdentNode(getIdent(c.cache, "nimAtCompileTimeExit"), n.info))) # no need for an expensive 'try' if we stop after the first error anyway: if c.config.errorMax <= 1: result = semStmtAndGenerateGenerics(c, n) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 605df443ecb5b..d71ef85517a70 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1816,3 +1816,16 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = result.add ni else: break else: break + +var compileTimeExitProcs {.compileTime.}: seq[proc(): NimNode] +proc addCompileTimeExitProc*(cb: proc(): NimNode) {.compileTime.} = + ## Schedules `cb` to be executed as if it was a macro call + ## at the end of main module. Appends result of `cb` at the + ## end of main module. + if compileTimeExitProcs.len == 0: + nimSetCompileTimeExit(proc(): NimNode = + result = newNimNode(nnkStmtList) + for cb in compileTimeExitProcs: + result.add(cb())) + compileTimeExitProcs.add(cb) + diff --git a/lib/system.nim b/lib/system.nim index a18e81d3d7130..3477d0385cf3d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3087,3 +3087,12 @@ proc arrayWithDefault*[T](size: static int): array[size, T] {.noinit, nodestroy, ## Creates a new array filled with `default(T)`. for i in 0..size-1: result[i] = default(T) + +var compileTimeExitProc {.compileTime.}: proc(): NimNode + +proc nimSetCompileTimeExit*(cb: proc(): NimNode) {.compileTime.} = + compileTimeExitProc = cb + +macro nimAtCompileTimeExit*(): untyped = + if compileTimeExitProc != nil: + result = compileTimeExitProc() diff --git a/tests/macros/tctexit.nim b/tests/macros/tctexit.nim new file mode 100644 index 0000000000000..9ae804c32514e --- /dev/null +++ b/tests/macros/tctexit.nim @@ -0,0 +1,20 @@ +discard """ + output: "Hello\nTest passed: 11" +""" +import macros + +var foo {.compileTime.} = 0 + +static: + addCompileTimeExitProc() do() -> NimNode: + let f = ident"foo" + result = quote do: + proc test() = + echo "Test passed: ", static(foo) + test() + +static: + foo = 10 + inc foo + +echo "Hello"