summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim4
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/ccgstmts.nim4
-rw-r--r--lib/core/strs.nim10
-rw-r--r--tests/destructor/tnewruntime_misc.nim6
5 files changed, 26 insertions, 2 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 6238acb14..48e8afa67 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -759,7 +759,9 @@ type
     lfImportCompilerProc,     # ``importc`` of a compilerproc
     lfSingleUse               # no location yet and will only be used once
     lfEnforceDeref            # a copyMem is required to dereference if this a
-      # ptr array due to C array limitations. See #1181, #6422, #11171
+                              # ptr array due to C array limitations.
+                              # See #1181, #6422, #11171
+    lfPrepareForMutation      # string location is about to be mutated (V2)
   TStorageLoc* = enum
     OnUnknown,                # location is unknown (stack, heap or static)
     OnStatic,                 # in a static section
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index a9932a0ce..224054506 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -944,6 +944,10 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
     a.r = ropecg(p.module, "(*$1)", [a.r])
+
+  if lfPrepareForMutation in d.flags and ty.kind == tyString and
+      p.config.selectedGC == gcDestructors:
+    linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
   putIntoDest(p, d, n,
               ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
 
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index bf07c1af7..337b36904 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1266,11 +1266,13 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
     discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
     initLoc(a, locNone, le, OnUnknown)
     a.flags.incl(lfEnforceDeref)
+    a.flags.incl(lfPrepareForMutation)
     expr(p, le, a)
+    a.flags.excl(lfPrepareForMutation)
     if fastAsgn: incl(a.flags, lfNoDeepCopy)
     assert(a.t != nil)
     genLineDir(p, ri)
-    loadInto(p, e.sons[0], ri, a)
+    loadInto(p, le, ri, a)
   else:
     genLineDir(p, e)
     asgnFieldDiscriminant(p, e)
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
index 677e8731d..92c39331f 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -189,3 +189,13 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
       a.p.cap = b.len
     a.len = b.len
     copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
+
+proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl.} =
+  if s.p != nil and s.p.allocator == nil:
+    let oldP = s.p
+    # can't mutate a literal, so we need a fresh copy here:
+    let allocator = getLocalAllocator()
+    s.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(s.len)))
+    s.p.allocator = allocator
+    s.p.cap = s.len
+    copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim
index 029a2a78d..2bdae5c8b 100644
--- a/tests/destructor/tnewruntime_misc.nim
+++ b/tests/destructor/tnewruntime_misc.nim
@@ -2,6 +2,7 @@ discard """
   cmd: '''nim cpp --newruntime $file'''
   output: '''(field: "value")
 Indeed
+axc
 0  new: 0'''
 """
 
@@ -24,6 +25,11 @@ proc main =
   echo w["key"][]
   echo getEnv("HEAPTRASHING")
 
+  # bug #11891
+  var x = "abc"
+  x[1] = 'x'
+  echo x
+
 main()
 
 # bug #11745