summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgstmts.nim27
-rw-r--r--compiler/enumtostr.nim56
-rw-r--r--lib/system/chcks.nim5
-rw-r--r--tests/destructor/tcaseobj_transitions.nim33
4 files changed, 120 insertions, 1 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 7552f0c63..eed046afd 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1220,6 +1220,21 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
         [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
          intLiteral(L+1)])
 
+proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
+  const ObjDiscMappingProcSlot = -5
+  var theProc: PSym = nil
+  for idx, p in items(t.methods):
+    if idx == ObjDiscMappingProcSlot:
+      theProc = p
+      break
+  if theProc == nil:
+    theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph)
+    t.methods.add((ObjDiscMappingProcSlot, theProc))
+  var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
+  call.add newSymNode(theProc)
+  call.add e
+  expr(p, call, d)
+
 proc asgnFieldDiscriminant(p: BProc, e: PNode) =
   var a, tmp: TLoc
   var dotExpr = e.sons[0]
@@ -1227,7 +1242,17 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
   initLocExpr(p, e.sons[0], a)
   getTemp(p, a.t, tmp)
   expr(p, e.sons[1], tmp)
-  genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, dotExpr.sons[1].sym)
+  let field = dotExpr.sons[1].sym
+  if optNimV2 in p.config.globalOptions:
+    let t = dotExpr[0].typ.skipTypes(abstractInst)
+    var oldVal, newVal: TLoc
+    genCaseObjDiscMapping(p, e[0], t, field, oldVal)
+    genCaseObjDiscMapping(p, e[1], t, field, newVal)
+    lineCg(p, cpsStmts,
+          "#nimFieldDiscriminantCheckV2($1, $2);$n",
+          [rdLoc(oldVal), rdLoc(newVal)])
+  else:
+    genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, field)
   genAssignment(p, a, tmp, {})
 
 proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
index 34d5f2d66..930cf24c4 100644
--- a/compiler/enumtostr.nim
+++ b/compiler/enumtostr.nim
@@ -40,3 +40,59 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
   n.sons[resultPos] = newSymNode(res)
   result.ast = n
   incl result.flags, sfFromGeneric
+
+proc searchObjCase(obj: PNode; field: PSym): PNode =
+  case obj.kind
+  of nkSym:
+    result = nil
+  of nkElse, nkOfBranch:
+    result = searchObjCase(obj.lastSon, field)
+  else:
+    if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
+      result = obj
+    else:
+      for x in obj:
+        result = searchObjCase(x, field)
+        if result != nil: break
+
+proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph): PSym =
+  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), t.owner, info)
+
+  let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
+  dest.typ = field.typ
+
+  let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
+  res.typ = getSysType(g, info, tyUInt8)
+
+  result.typ = newType(tyProc, t.owner)
+  result.typ.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result.typ, res.typ)
+  addSon(result.typ.n, newNodeI(nkEffectList, info))
+
+  result.typ.addParam dest
+
+  var body = newNodeI(nkStmtList, info)
+  var caseStmt = newNodeI(nkCaseStmt, info)
+  caseStmt.add(newSymNode dest)
+
+  let subObj = searchObjCase(t.n, field)
+  doAssert subObj != nil
+  for i in 1 ..< subObj.len:
+    let ofBranch = subObj[i]
+    var newBranch = newNodeI(ofBranch.kind, ofBranch.info)
+    for j in 0..ofBranch.len-2:
+      newBranch.add ofBranch[j]
+
+    newBranch.add newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newIntNode(nkInt8Lit, i)))
+    caseStmt.add newBranch
+
+  body.add(caseStmt)
+
+  var n = newNodeI(nkProcDef, info, bodyPos+2)
+  for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
+  n.sons[namePos] = newSymNode(result)
+  n.sons[paramsPos] = result.typ.n
+  n.sons[bodyPos] = body
+  n.sons[resultPos] = newSymNode(res)
+  result.ast = n
+  incl result.flags, sfFromGeneric
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 303a8945a..d01dfec9a 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -114,3 +114,8 @@ when not defined(nimV2):
       if x == nil: return false
       x = x.base
     return true
+
+when defined(nimV2):
+  proc nimFieldDiscriminantCheckV2(oldDiscVal, newDiscVal: uint8) {.compilerProc.} =
+    if oldDiscVal != newDiscVal:
+      sysFatal(FieldError, "assignment to discriminant changes object branch")
diff --git a/tests/destructor/tcaseobj_transitions.nim b/tests/destructor/tcaseobj_transitions.nim
new file mode 100644
index 000000000..9377d57b0
--- /dev/null
+++ b/tests/destructor/tcaseobj_transitions.nim
@@ -0,0 +1,33 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  output: '''no crash'''
+"""
+
+# bug #11205
+
+type
+  MyEnum = enum
+    A, B, C
+  MyCaseObject = object
+    case kind: MyEnum
+    of A: iseq: seq[int]
+    of B: fseq: seq[float]
+    of C: str: string
+
+
+  MyCaseObjectB = object # carefully constructed to use the same enum,
+                         # but a different object type!
+    case kind: MyEnum
+    of A, C: x: int
+    of B: fseq: seq[float]
+
+
+var x = MyCaseObject(kind: A)
+x.iseq.add 1
+#x.kind = B
+#x.fseq.add -3.0
+
+var y = MyCaseObjectB(kind: A)
+y.x = 1
+y.kind = C
+echo "no crash"