diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 1b7979bb4a..c6e0188a44 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -838,8 +838,12 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = else: initializer = value genGlobalVarDecl(p.module.s[cfsVars], p, n, td, initializer = initializer) - if p.withinLoop > 0 and value == "": + if p.withinLoop > 0 and value == "" and + s.loc.t.skipTypes(abstractInst).kind notin {tyVar, tyLent}: # fixes tests/run/tzeroarray: + # Don't reset borrowed references (var/lent): the pointer itself is still + # uninitialized here, so resetLoc would dereference garbage. Such variables + # (e.g. the loop var of `mitems`) are always assigned before use anyway. backendEnsureMutable s resetLoc(p, s.locImpl) diff --git a/tests/arc/tmitems_loopvar.nim b/tests/arc/tmitems_loopvar.nim new file mode 100644 index 0000000000..0d63f06fcd --- /dev/null +++ b/tests/arc/tmitems_loopvar.nim @@ -0,0 +1,28 @@ +discard """ + output: '''a +b +c +a +b +c''' +""" + +# A `var T` loop variable (here from `mitems`) declared inside an enclosing +# loop must not be reset by dereferencing: at module scope it is emitted as a +# global, and the in-loop reset path used `resetLoc`, which dereferenced the +# still-uninitialized borrowed pointer and crashed with a SIGSEGV. + +for p in @["abc", "123"]: + var testA = @["a", "b", "c"] + for l in testA.mitems: + echo(l) + +# also exercise actual mutation through the borrowed reference +block: + var ok = true + for p in @["x", "y"]: + var s = @[1, 2, 3] + for l in s.mitems: + l += 10 + if s != @[11, 12, 13]: ok = false + doAssert ok