From 8a411da772660075e9890437950ab61b00f33fc2 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:49:48 +0800 Subject: [PATCH] fixes #26045; #26046; `when nimvm` leak push options (#26047) fixes #26045; fixes #26046 The fix isolates compiler option state while semantically checking each when nimvm branch. compiler/semexprs.nim:2745 snapshots the option stack, compiler options, diagnostics settings, and enabled features. It analyzes one branch and restores that state in finally. Both the nimvm and else branches use this function. This prevents: ```nim when nimvm: {.push overflowChecks: off.} ``` from disabling overflow checks in following runtime code. It also means a {.pop.} in the opposite branch correctly reports that it has no corresponding {.push.}. (cherry picked from commit f6651e6c705300c48f7b846e998a7a02623d151f) --- compiler/semexprs.nim | 21 +++++++++++++++++++-- tests/whenstmt/twhen_nimvm_push.nim | 21 +++++++++++++++++++++ tests/whenstmt/twhen_nimvm_push_pop.nim | 8 ++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/whenstmt/twhen_nimvm_push.nim create mode 100644 tests/whenstmt/twhen_nimvm_push_pop.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6599dd028d..683bde602a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2664,6 +2664,22 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P else: result = semDirectOp(c, n, flags, expectedType) +proc semNimvmBranch(c: PContext, n: PNode, flags: TExprFlags): PNode = + let + oldOptionStack = c.optionStack[0..^1] + oldOptions = c.config.options + oldNotes = c.config.notes + oldWarningAsErrors = c.config.warningAsErrors + oldFeatures = c.features + try: + result = semExpr(c, n, flags) + finally: + c.optionStack = oldOptionStack + c.config.options = oldOptions + c.config.notes = oldNotes + c.config.warningAsErrors = oldWarningAsErrors + c.features = oldFeatures + proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # If semCheck is set to false, ``when`` will return the verbatim AST of # the correct branch. Otherwise the AST will be passed through semStmt. @@ -2700,7 +2716,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = checkSonsLen(it, 2, c.config) if whenNimvm: if semCheck: - it[1] = semExpr(c, it[1], flags) + it[1] = semNimvmBranch(c, it[1], flags) typ = commonType(c, typ, it[1].typ) result = n # when nimvm is not elimited until codegen elif c.inGenericContext > 0: @@ -2731,7 +2747,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = discard elif result == nil or whenNimvm: if semCheck: - it[0] = semExpr(c, it[0], flags) + it[0] = if whenNimvm: semNimvmBranch(c, it[0], flags) + else: semExpr(c, it[0], flags) typ = commonType(c, typ, it[0].typ) if typ != nil and typ.kind != tyUntyped: it[0] = fitNode(c, typ, it[0], it[0].info) diff --git a/tests/whenstmt/twhen_nimvm_push.nim b/tests/whenstmt/twhen_nimvm_push.nim new file mode 100644 index 0000000000..7991239fcc --- /dev/null +++ b/tests/whenstmt/twhen_nimvm_push.nim @@ -0,0 +1,21 @@ +discard """ + output: "ok" +""" + +var overflowDetected = false +when nimvm: + {.push overflowChecks: off.} +else: + var branchX = high(int) + try: + inc branchX + except OverflowDefect: + overflowDetected = true + +doAssert overflowDetected + +var x = high(int) +try: + inc x +except OverflowDefect: + echo "ok" diff --git a/tests/whenstmt/twhen_nimvm_push_pop.nim b/tests/whenstmt/twhen_nimvm_push_pop.nim new file mode 100644 index 0000000000..31f44c4058 --- /dev/null +++ b/tests/whenstmt/twhen_nimvm_push_pop.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "{.pop.} without a corresponding {.push.}" + line: 8 +""" + +when nimvm: + {.push checks: off.} +else: {.pop.}