diff --git a/compiler/vm.nim b/compiler/vm.nim index 778583a31d..fc9da48f37 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -12,7 +12,7 @@ import semmacrosanity import - std/[strutils, tables, parseutils], + std/[strutils, tables, intsets, parseutils], msgs, vmdef, vmgen, nimsets, types, parser, vmdeps, idents, trees, renderer, options, transf, gorgeimpl, lineinfos, btrees, macrocacheimpl, @@ -2425,9 +2425,12 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator; setupGlobalCtx(module, g, idgen) var c = PCtx g.vm let oldMode = c.mode + let oldLocals = c.locals c.mode = mode + c.locals = initIntSet() c.cannotEval = false let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) + c.locals = oldLocals if c.cannotEval: return errorNode(idgen, prc, n) if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 3c39661127..f7e18d3010 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,7 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import std/[tables, strutils] +import std/[tables, strutils, intsets] import ast, idents, options, modulegraphs, lineinfos @@ -272,6 +272,7 @@ type vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC) procToCodePos*: Table[int, int] cannotEval*: bool + locals*: IntSet PStackFrame* = ref TStackFrame TStackFrame* {.acyclic.} = object diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0db0e93f9f..6e47f6fe44 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1583,6 +1583,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # are in the right scope: if sfGenSym in s.flags and c.prc.sym == nil: discard elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard + elif s.kind in {skVar, skLet} and s.id in c.locals: discard else: cannotEval(c, n) elif s.kind in {skProc, skFunc, skConverter, skMethod, skIterator} and sfForward in s.flags: @@ -1975,7 +1976,7 @@ proc genVarSection(c: PCtx; n: PNode) = c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner)) elif a[0].kind == nkSym: let s = a[0].sym - checkCanEval(c, a[0]) + c.locals.incl(s.id) if s.isGlobal: let runtimeAccessToCompileTime = c.mode == emRepl and sfCompileTime in s.flags and s.position > 0 diff --git a/tests/vm/tconststaticvar.nim b/tests/vm/tconststaticvar.nim new file mode 100644 index 0000000000..cd575663e5 --- /dev/null +++ b/tests/vm/tconststaticvar.nim @@ -0,0 +1,79 @@ +block: # issue #8758 + template baz() = + var i = 0 + + proc foo() = + static: + var i = 0 + baz() + +block: # issue #10828 + proc test(i: byte): bool = + const SET = block: # No issues when defined outside proc + var s: set[byte] + for i in 0u8 .. 255u8: incl(s, i) + s + return i in SET + doAssert test(0) + doAssert test(127) + doAssert test(255) + +block: # issue #12172 + const TEST = block: + var test: array[5, string] + for i in low(test)..high(test): + test[i] = $i + test + proc test = + const TEST2 = block: + var test: array[5, string] # Error here + for i in low(test)..high(test): + test[i] = $i + test + doAssert TEST == TEST2 + doAssert TEST == @["0", "1", "2", "3", "4"] + doAssert TEST2 == @["0", "1", "2", "3", "4"] + test() + +block: # issue #21610 + func stuff(): int = + const r = block: + var r = 1 # Error: cannot evaluate at compile time: r + for i in 2..10: + r *= i + r + r + doAssert stuff() == 3628800 + +block: # issue #23803 + func foo1(c: int): int {.inline.} = + const arr = block: + var res: array[0..99, int] + res[42] = 43 + res + arr[c] + doAssert foo1(41) == 0 + doAssert foo1(42) == 43 + doAssert foo1(43) == 0 + + # works + func foo2(c: int): int {.inline.} = + func initArr(): auto = + var res: array[0..99, int] + res[42] = 43 + res + const arr = initArr() + arr[c] + doAssert foo2(41) == 0 + doAssert foo2(42) == 43 + doAssert foo2(43) == 0 + + # also works + const globalArr = block: + var res: array[0..99, int] + res[42] = 43 + res + func foo3(c: int): int {.inline.} = globalArr[c] + doAssert foo3(41) == 0 + doAssert foo3(42) == 43 + doAssert foo3(43) == 0 diff --git a/tests/vm/tconststaticvar2.nim b/tests/vm/tconststaticvar2.nim new file mode 100644 index 0000000000..83b8309f0a --- /dev/null +++ b/tests/vm/tconststaticvar2.nim @@ -0,0 +1,13 @@ +# issue #24634 + +type J = object + +template m(u: J): int = + let v = u + 0 + +proc g() = + const x = J() + const _ = m(x) + +g() diff --git a/tests/vm/tconststaticvar3.nim b/tests/vm/tconststaticvar3.nim new file mode 100644 index 0000000000..0480450141 --- /dev/null +++ b/tests/vm/tconststaticvar3.nim @@ -0,0 +1,9 @@ +# issue #24633 + +import std/sequtils + +proc f(a: static openArray[int]) = + const s1 = a.mapIt(it) + const s2 = a.toSeq() + +f([1,2,3]) diff --git a/tests/vm/tconststaticvar_wrong.nim b/tests/vm/tconststaticvar_wrong.nim new file mode 100644 index 0000000000..091ff39c4f --- /dev/null +++ b/tests/vm/tconststaticvar_wrong.nim @@ -0,0 +1,6 @@ +proc test = + const TEST = block: + let i = 1 + const j = i + 1 #[tt.Error + ^ cannot evaluate at compile time: i]# + j