From 9cfc5cfbeb30d9d58660d5dcec6b19a690a36005 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:07:15 +0800 Subject: [PATCH] fixes #26010; Double destroy with {.cursor.} (#26031) fixes #26010 Cursors do not own their values and therefore cannot transfer ownership through move. Reject move(cursor) during semantic analysis and share the cursor-location check between semantic analysis and destructor injection. (cherry picked from commit 99a696e0c469b12c0f4587e3caf4fce87e11c8c0) --- compiler/injectdestructors.nim | 13 +------------ compiler/semmagic.nim | 5 +++++ compiler/trees.nim | 11 +++++++++++ tests/arc/t26010.nim | 23 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 tests/arc/t26010.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 42db12efd8..7b105819ed 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -24,7 +24,7 @@ import std/[strtabs, tables, strutils, intsets] when defined(nimPreviewSlimSystem): import std/assertions -from trees import exprStructuralEquivalent, getRoot, whichPragma +from trees import exprStructuralEquivalent, getRoot, isCursor, whichPragma type Con = object @@ -180,17 +180,6 @@ proc isFirstWrite(n: PNode; c: var Con): bool = let m = skipConvDfa(n) result = nfFirstWrite in m.flags -proc isCursor(n: PNode): bool = - case n.kind - of nkSym: - sfCursor in n.sym.flags - of nkDotExpr: - isCursor(n[1]) - of nkCheckedFieldExpr: - isCursor(n[0]) - else: - false - template isFullyUnpackedTuple(n: PNode): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index bb2c2b32e0..4dc621de1f 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -691,5 +691,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[1].kind in {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt}: localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'") + of mMove: + result = n + if isCursor(n[1]): + localError(c.config, n.info, errFailedMove, + "cannot move cursor '" & $n[1] & "'; a cursor does not own its value") else: result = n diff --git a/compiler/trees.nim b/compiler/trees.nim index a3fb36cf7c..6ecfad67c3 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -225,6 +225,17 @@ proc getRoot*(n: PNode): PSym = else: result = nil else: result = nil +proc isCursor*(n: PNode): bool = + case n.kind + of nkSym: + sfCursor in n.sym.flags + of nkDotExpr: + isCursor(n[1]) + of nkCheckedFieldExpr: + isCursor(n[0]) + else: + false + proc stupidStmtListExpr*(n: PNode): bool = for i in 0..