don't evaluate "cannot eval" errors with nim check (#24289)

fixes #24288, refs #23625

Since #23625 "cannot evaluate" errors during VM code generation are
"soft" errors with `nim check`, i.e. the code generation isn't halted
(except for the current proc which `return`s which can cause wrong
codegen) and the expression is still attempted to be evaluated. Now,
these errors signal to the VM that the current generated VM code cannot
be evaluated, and so instead of evaluating, an error node is returned.
This keeps the benefit of the "soft" errors without potentially crashing
the compiler on improperly generated VM code. Although maybe the
compiler might not be able to handle the generated error node in some
cases.

This fixes the chame example in #24288 but this is not tested in CI.
Presumably it or the compiler was doing something like `compiles()` on
code that can't run in the VM.

I would accept nicer ways of tracking non-evaluability than
`c.cannotEval = true` but I tried to keep it as harmless as possible.

(cherry picked from commit def1fea43a)
This commit is contained in:
metagn
2024-10-12 23:39:59 +03:00
committed by narimiran
parent d357a2e9a5
commit bffd2e0330
3 changed files with 19 additions and 5 deletions

View File

@@ -2332,9 +2332,17 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
localError(c.config, sym.info,
"NimScript: attempt to call non-routine: " & sym.name.s)
proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
result.typ = newType(tyError, idgen, owner)
result.typ.flags.incl tfCheckedForDestructor
proc evalStmt*(c: PCtx, n: PNode) =
let n = transformExpr(c.graph, c.idgen, c.module, n)
let start = genStmt(c, n)
if c.cannotEval:
c.cannotEval = false
return
# execute new instructions; this redundant opcEof check saves us lots
# of allocations in 'execute':
if c.code[start].opcode != opcEof:
@@ -2345,7 +2353,10 @@ proc evalExpr*(c: PCtx, n: PNode): PNode =
# `nim --eval:"expr"` might've used it at some point for idetools; could
# be revived for nimsuggest
let n = transformExpr(c.graph, c.idgen, c.module, n)
c.cannotEval = false
let start = genExpr(c, n)
if c.cannotEval:
return errorNode(c.idgen, c.module, n)
assert c.code[start].opcode != opcEof
result = execute(c, start)
@@ -2398,7 +2409,10 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
var c = PCtx g.vm
let oldMode = c.mode
c.mode = mode
c.cannotEval = false
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
if c.cannotEval:
return errorNode(idgen, prc, n)
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
assert c.code[start].opcode != opcEof
when debugEchoCode: c.echoCode start
@@ -2469,11 +2483,6 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
# to prevent endless recursion in macro instantiation
const evalMacroLimit = 1000
#proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
# result = newNodeI(nkEmpty, n.info)
# result.typ = newType(tyError, idgen, owner)
# result.typ.flags.incl tfCheckedForDestructor
proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
n, nOrig: PNode, sym: PSym): PNode =
#if g.config.errorCounter > 0: return errorNode(idgen, module, n)
@@ -2497,7 +2506,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
c.comesFromHeuristic.line = 0'u16
c.callsite = nOrig
c.templInstCounter = templInstCounter
c.cannotEval = false
let start = genProc(c, sym)
if c.cannotEval:
return errorNode(idgen, module, n)
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
let maxSlots = sym.offset

View File

@@ -270,6 +270,7 @@ type
templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
procToCodePos*: Table[int, int]
cannotEval*: bool
PStackFrame* = ref TStackFrame
TStackFrame* {.acyclic.} = object

View File

@@ -1545,6 +1545,7 @@ template cannotEval(c: PCtx; n: PNode) =
if c.config.cmd == cmdCheck:
localError(c.config, n.info, "cannot evaluate at compile time: " &
n.renderTree)
c.cannotEval = true
return
globalError(c.config, n.info, "cannot evaluate at compile time: " &
n.renderTree)