mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
track introduced locals in vmgen for eval check (#24674)
fixes #8758, fixes #10828, fixes #12172, fixes #21610, fixes #23803,
fixes #24633, fixes #24634, succeeds #24085
We simply track the symbol ID of every traversed `var`/`let` definition
in `vmgen`, then these symbols are always considered evaluable in the
current `vmgen` context. The set of symbols is reset before every
generation, but both tests worked properly without doing this including
the nested `const`, so maybe it's already done in some way I'm not
seeing.
(cherry picked from commit a5cc33c1d3)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
79
tests/vm/tconststaticvar.nim
Normal file
79
tests/vm/tconststaticvar.nim
Normal file
@@ -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
|
||||
13
tests/vm/tconststaticvar2.nim
Normal file
13
tests/vm/tconststaticvar2.nim
Normal file
@@ -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()
|
||||
9
tests/vm/tconststaticvar3.nim
Normal file
9
tests/vm/tconststaticvar3.nim
Normal file
@@ -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])
|
||||
6
tests/vm/tconststaticvar_wrong.nim
Normal file
6
tests/vm/tconststaticvar_wrong.nim
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user