summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorringabout <43030857+ringabout@users.noreply.github.com>2023-09-08 23:05:57 +0800
committerGitHub <noreply@github.com>2023-09-08 17:05:57 +0200
commit5f13e15e0a6f90c462a71cd30addc677f688c4dc (patch)
tree570db0a6752e27908af4e85347834b68a577a43b
parentd45270bdf721e195a9dae344f9a3285d066c3932 (diff)
downloadNim-5f13e15e0a6f90c462a71cd30addc677f688c4dc.tar.gz
fixes #22664; guard against potential seqs self assignments (#22671)
fixes #22664
-rw-r--r--compiler/liftdestructors.nim11
-rw-r--r--lib/system/seqs_v2.nim6
-rw-r--r--tests/arc/tarcmisc.nim21
3 files changed, 38 insertions, 0 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 387a22555..a73df046e 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -553,6 +553,14 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   else:
     body.sons.setLen counterIdx
 
+proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
+      )
+  cond.typ = getSysType(c.g, c.info, tyBool)
+  body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
+
 proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedDup:
@@ -560,10 +568,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     forallElements(c, t, body, x, y)
   of attachedAsgn, attachedDeepCopy:
     # we generate:
+    # if x.p == y.p:
+    #   return
     # setLen(dest, y.len)
     # var i = 0
     # while i < y.len: dest[i] = y[i]; inc(i)
     # This is usually more efficient than a destroy/create pair.
+    checkSelfAssignment(c, t, body, x, y)
     body.add setLenSeqCall(c, t, x, y)
     forallElements(c, t, body, x, y)
   of attachedSink:
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
index 3a49142bf..b71b81762 100644
--- a/lib/system/seqs_v2.nim
+++ b/lib/system/seqs_v2.nim
@@ -32,6 +32,10 @@ type
     len: int
     p: ptr NimSeqPayload[T]
 
+  NimRawSeq = object
+    len: int
+    p: pointer
+
 const nimSeqVersion {.core.} = 2
 
 # XXX make code memory safe for overflows in '*'
@@ -139,6 +143,8 @@ proc newSeq[T](s: var seq[T], len: Natural) =
   shrink(s, 0)
   setLen(s, len)
 
+proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} =
+  result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
 
 template capacityImpl(sek: NimSeqV2): int =
   if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0
diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim
index 1404f54a1..525c8c3a6 100644
--- a/tests/arc/tarcmisc.nim
+++ b/tests/arc/tarcmisc.nim
@@ -614,3 +614,24 @@ method process*(self: App): Option[Event] {.base.} =
 type Test2 = ref object of RootObj
 
 method bug(t: Test2): seq[float] {.base.} = discard
+
+block: # bug #22664
+  type
+    ElementKind = enum String, Number
+    Element = object
+      case kind: ElementKind
+      of String:
+        str: string
+      of Number:
+        num: float
+    Calc = ref object
+      stack: seq[Element]
+
+  var calc = new Calc
+
+  calc.stack.add Element(kind: Number, num: 200.0)
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  let calc2 = calc
+  calc2.stack = calc.stack # This nulls out the object in the stack
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]"