mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-17 09:01:18 +00:00
fix: {.cast(uncheckedAssign).} ineffective across yield in closure iterators (#25916)
closureiters.nim splits a stmt list at yield points, moving post-yield
code into a new state body. When that stmt list was inside a pragma
block like `{.cast(uncheckedAssign).}`, the new state's body was created
as a bare nkStmtList without the wrapper.
Fix: track the enclosing pragma block in the transform context, and wrap
newly-created state bodies in a copy of it when the split occurs inside
one. Added an explicit `nkPragmaBlock` case to
`transformClosureIteratorBody` that saves/restores `ctx.enclosingPragma`
around its body.
This commit is contained in:
@@ -167,6 +167,8 @@ type
|
||||
curExcSym: PSym # Current exception
|
||||
externExcSym: PSym # Extern exception: what would getCurrentException() return outside of closure iter
|
||||
|
||||
enclosingPragmas: seq[PNode] # stack of pragma blocks wrapping stmtlist
|
||||
|
||||
states: seq[State] # The resulting states. Label is int literal.
|
||||
finallyPathStack: seq[FinallyTarget] # Stack of split blocks, whiles and finallies
|
||||
stateLoopLabel: PSym # Label to break on, when jumping between states.
|
||||
@@ -983,9 +985,14 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
for j in i + 1..<n.len:
|
||||
s.add(n[j])
|
||||
|
||||
var body = s
|
||||
for pragma in ctx.enclosingPragmas:
|
||||
body = newTreeI(nkPragmaBlock, n[i + 1].info,
|
||||
pragma[0].copyTree, body)
|
||||
|
||||
n.sons.setLen(i + 1)
|
||||
discard ctx.newState(s, true, label)
|
||||
if ctx.transformClosureIteratorBody(s, gotoOut) != s:
|
||||
discard ctx.newState(body, true, label)
|
||||
if ctx.transformClosureIteratorBody(body, gotoOut) != body:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != s")
|
||||
break
|
||||
else:
|
||||
@@ -1123,6 +1130,14 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
finallyBody = ctx.transformClosureIteratorBody(finallyBody, finallyExit)
|
||||
dec ctx.curFinallyLevel
|
||||
|
||||
of nkPragmaBlock:
|
||||
# Propagate the pragma blocks so that blocks like {.cast(uncheckedAssign).}
|
||||
# remain effective
|
||||
ctx.enclosingPragmas.add(n)
|
||||
n[1] = ctx.transformClosureIteratorBody(n[1], gotoOut)
|
||||
discard ctx.enclosingPragmas.pop()
|
||||
result = n
|
||||
|
||||
of nkGotoState, nkForStmt:
|
||||
internalError(ctx.g.config, "closure iter " & $n.kind)
|
||||
|
||||
|
||||
52
tests/iter/tlowerpragmablock.nim
Normal file
52
tests/iter/tlowerpragmablock.nim
Normal file
@@ -0,0 +1,52 @@
|
||||
discard """
|
||||
action: "run"
|
||||
"""
|
||||
|
||||
# Test: {.cast(uncheckedAssign).} must suppress FieldDiscriminantCheck
|
||||
# when a yield inside the pragma block causes the closure-iterator
|
||||
# transform to split the body. The discriminant assignment lands in
|
||||
# the post-yield state and must inherit the wrapper.
|
||||
|
||||
type
|
||||
MyKind = enum mkOne, mkTwo
|
||||
MyVariant = object
|
||||
case kind: MyKind
|
||||
of mkOne:
|
||||
x: int
|
||||
of mkTwo:
|
||||
y: float
|
||||
|
||||
iterator iterUncheckedYield(dest: var MyVariant): int {.closure.} =
|
||||
{.cast(uncheckedAssign).}:
|
||||
yield 1
|
||||
dest.kind = mkTwo
|
||||
|
||||
block:
|
||||
var v = MyVariant(kind: mkOne, x: 42)
|
||||
var count = 0
|
||||
for x in iterUncheckedYield(v):
|
||||
if count == 0:
|
||||
doAssert x == 1
|
||||
inc count
|
||||
# don't break — continue to advance past the yield,
|
||||
# which runs the discriminant assignment
|
||||
else:
|
||||
break
|
||||
doAssert v.kind == mkTwo
|
||||
|
||||
iterator iterNestedPragma(dest: var MyVariant): int {.closure.} =
|
||||
{.cast(uncheckedAssign).}:
|
||||
{.cast(gcsafe).}:
|
||||
yield 1
|
||||
dest.kind = mkTwo
|
||||
|
||||
block:
|
||||
var v = MyVariant(kind: mkOne, x: 42)
|
||||
var count = 0
|
||||
for x in iterNestedPragma(v):
|
||||
if count == 0:
|
||||
doAssert x == 1
|
||||
inc count
|
||||
else:
|
||||
break
|
||||
doAssert v.kind == mkTwo
|
||||
Reference in New Issue
Block a user