mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-17 10:34:53 +00:00
fix SIGSEGV on refc: genGenericAsgn must deep-copy from static data (#26160)
Supersedes #26120 (https://github.com/nim-lang/Nim/pull/26120), which fixed the reported case but left the `tfShallow` half of the same condition open. Since #25860 array literals are materialized into temporaries so that `lent` results keep valid backing storage. In an async proc such a temporary is lifted into the closure environment, and the environment is filled with an `nkFastAsgn`, i.e. without `needToCopy`. `genGenericAsgn` then emitted `genericShallowAssign` from the static const array, and for the `string` elements that ends in `unsureAsgnRef` -> `incRef(usrToCell(literal))` on memory that has no GC header: proc f() {.async.} = for ip in ["::1", "2001:db8::", "::"]: await sleepAsync(1) `OnStatic` sources must therefore always take the `genericAssign` path. Note that the guard has to dominate the `tfShallow` test as well, not just the `needToCopy` one: a `{.shallow.}` destination assigned from a `const` crashes in exactly the same way. This is the precedence `genOptAsgnTuple` and `genOptAsgnObject` already use, so `genGenericAsgn` now agrees with its two siblings instead of contradicting them. refc only; the other GCs do not reference count in `unsureAsgnRef`. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -251,8 +251,9 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# (for objects, etc.):
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
simpleAsgn(p.s(cpsStmts), dest, src)
|
||||
elif needToCopy notin flags or
|
||||
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
|
||||
elif src.storage != OnStatic and
|
||||
(needToCopy notin flags or
|
||||
tfShallow in skipTypes(dest.t, abstractVarRange).flags):
|
||||
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
|
||||
let rad = addrLoc(p.config, dest)
|
||||
let ras = addrLoc(p.config, src)
|
||||
|
||||
32
tests/assign/tshallow_asgn_from_static.nim
Normal file
32
tests/assign/tshallow_asgn_from_static.nim
Normal file
@@ -0,0 +1,32 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
output: '''aaaeee
|
||||
["p", "q", "r"]'''
|
||||
"""
|
||||
|
||||
# A `{.shallow.}` destination type must not shortcut the deep copy when the
|
||||
# source is static data: `genericShallowAssign` would `incRef` the string
|
||||
# literals, which have no GC header. `genOptAsgnTuple`/`genOptAsgnObject`
|
||||
# already give `OnStatic` priority over `tfShallow`; `genGenericAsgn` has to
|
||||
# agree with them.
|
||||
|
||||
type
|
||||
Obj {.shallow.} = object
|
||||
a, b, c, d, e: string # asgnComplexity > 4, so genGenericAsgn is used
|
||||
ShallowArray {.shallow.} = array[3, string]
|
||||
Holder = ref object
|
||||
o: Obj
|
||||
a: ShallowArray
|
||||
|
||||
const
|
||||
c1 = Obj(a: "aaa", b: "bbb", c: "ccc", d: "ddd", e: "eee")
|
||||
ca: ShallowArray = ["p", "q", "r"]
|
||||
|
||||
proc main =
|
||||
var h = Holder()
|
||||
h.o = c1
|
||||
h.a = ca
|
||||
echo h.o.a, h.o.e
|
||||
echo h.a
|
||||
|
||||
main()
|
||||
37
tests/async/tawait_in_array_literal_loop.nim
Normal file
37
tests/async/tawait_in_array_literal_loop.nim
Normal file
@@ -0,0 +1,37 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
output: "ok"
|
||||
"""
|
||||
|
||||
# the loop temp lifted into the env must be deep-copied: a shallow assign from
|
||||
# the static const array made refc incRef string literals in static memory
|
||||
|
||||
import std/asyncdispatch
|
||||
|
||||
const ips = ["::1", "2001:db8::", "::"]
|
||||
|
||||
proc overLiteral(): Future[int] {.async.} =
|
||||
var n = 0
|
||||
for ip in ["::1", "2001:db8::", "::"]:
|
||||
await sleepAsync(1)
|
||||
n += ip.len
|
||||
return n
|
||||
|
||||
proc overConst(): Future[int] {.async.} =
|
||||
var n = 0
|
||||
for ip in ips:
|
||||
await sleepAsync(1)
|
||||
n += ip.len
|
||||
return n
|
||||
|
||||
proc overNested(): Future[int] {.async.} =
|
||||
var n = 0
|
||||
for x in [(1, "::1"), (2, "2001:db8::"), (3, "::")]:
|
||||
await sleepAsync(1)
|
||||
n += x[1].len
|
||||
return n
|
||||
|
||||
doAssert waitFor(overLiteral()) == 15
|
||||
doAssert waitFor(overConst()) == 15
|
||||
doAssert waitFor(overNested()) == 15
|
||||
echo "ok"
|
||||
Reference in New Issue
Block a user