summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim20
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--tests/vm/tcompiletimesideeffects.nim42
3 files changed, 56 insertions, 8 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index ddec457a1..45e259b65 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -710,16 +710,20 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
       let a = getConstExpr(c.module, n.sons[i], c.graph)
       if a == nil: return n
       call.add(a)
+
     #echo "NOW evaluating at compile time: ", call.renderTree
-    if sfCompileTime in callee.flags:
-      result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
-      if result.isNil:
-        localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
-      else: result = fixupTypeAfterEval(c, result, n)
+    if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
+      if sfCompileTime in callee.flags:
+        result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
+        if result.isNil:
+          localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
+        else: result = fixupTypeAfterEval(c, result, n)
+      else:
+        result = evalConstExpr(c.module, c.graph, call)
+        if result.isNil: result = n
+        else: result = fixupTypeAfterEval(c, result, n)
     else:
-      result = evalConstExpr(c.module, c.graph, call)
-      if result.isNil: result = n
-      else: result = fixupTypeAfterEval(c, result, n)
+      result = n
     #if result != n:
     #  echo "SUCCESS evaluated at compile time: ", call.renderTree
 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 04991193c..f60e2556d 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -548,6 +548,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
+  inc c.inStaticContext
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
     if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
@@ -607,6 +608,7 @@ proc semConst(c: PContext, n: PNode): PNode =
         v.ast = def[j]
         b.sons[j] = newSymNode(v)
     addSon(result,b)
+  dec c.inStaticContext
 
 include semfields
 
diff --git a/tests/vm/tcompiletimesideeffects.nim b/tests/vm/tcompiletimesideeffects.nim
new file mode 100644
index 000000000..4cd57b3bd
--- /dev/null
+++ b/tests/vm/tcompiletimesideeffects.nim
@@ -0,0 +1,42 @@
+discard """
+  output:
+'''
+@[0, 1, 2]
+@[3, 4, 5]
+@[0, 1, 2]
+3
+4
+'''
+"""
+
+template runNTimes(n: int, f : untyped) : untyped =
+  var accum: seq[type(f)]
+  for i in 0..n-1:
+    accum.add(f)
+  accum
+
+var state {.compileTime.} : int = 0
+proc fill(): int {.compileTime.} =
+  result = state
+  inc state
+
+# invoke fill() at compile time as a compile time expression
+const C1 = runNTimes(3, fill())
+echo C1
+
+# invoke fill() at compile time as a set of compile time statements
+const C2 =
+  block:
+    runNTimes(3, fill())
+echo C2
+
+# invoke fill() at compile time after a compile time reset of state
+const C3 =
+  block:
+    state = 0
+    runNTimes(3, fill())
+echo C3
+
+# evaluate fill() at compile time and use the results at runtime
+echo fill()
+echo fill()