diff options
-rw-r--r-- | compiler/ccgstmts.nim | 27 | ||||
-rw-r--r-- | compiler/enumtostr.nim | 56 | ||||
-rw-r--r-- | lib/system/chcks.nim | 5 | ||||
-rw-r--r-- | tests/destructor/tcaseobj_transitions.nim | 33 |
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" |