From e87910197a209d02cf69bba3aacd9766723aeb37 Mon Sep 17 00:00:00 2001 From: deech Date: Mon, 31 Dec 2018 07:41:24 -0600 Subject: [PATCH] Check there are no side effects before optimizing away compile time expressions. (#9934) --- compiler/semexprs.nim | 20 +++++++------ compiler/semstmts.nim | 2 ++ tests/vm/tcompiletimesideeffects.nim | 42 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tests/vm/tcompiletimesideeffects.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ddec457a1a..45e259b652 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 04991193cd..f60e2556d2 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 0000000000..4cd57b3bd6 --- /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()