diff options
author | cooldome <cdome@bk.ru> | 2020-04-16 20:04:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-16 21:04:05 +0200 |
commit | 9295251e68ee86b512de51a2f650729ac6893104 (patch) | |
tree | 93c1889f1d76cd99e9f7ea7a2040fe24a16947d3 | |
parent | b6f99409a967baebfda056a46f236643837e483b (diff) | |
download | Nim-9295251e68ee86b512de51a2f650729ac6893104.tar.gz |
Implements RFCs #209 (#13995)
* add test * add changelod entry Co-authored-by: cooldome <ariabushenko@bk.ru>
-rw-r--r-- | changelog.md | 29 | ||||
-rw-r--r-- | compiler/astalgo.nim | 4 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 31 | ||||
-rw-r--r-- | compiler/injectdestructors.nim | 73 | ||||
-rw-r--r-- | compiler/liftdestructors.nim | 174 | ||||
-rw-r--r-- | compiler/magicsys.nim | 25 | ||||
-rw-r--r-- | compiler/semdata.nim | 7 | ||||
-rw-r--r-- | tests/arc/tcaseobj.nim | 49 | ||||
-rw-r--r-- | tests/destructor/tgotoexceptions7.nim | 10 |
9 files changed, 293 insertions, 109 deletions
diff --git a/changelog.md b/changelog.md index d75e8d17b..90e014556 100644 --- a/changelog.md +++ b/changelog.md @@ -12,7 +12,34 @@ one in an instantiation context while `-d:nimIntHash1` recovers it globally. ## Language changes - +- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this: + ```nim + type + MyObj = object + case kind: bool + of true: y: ptr UncheckedArray[float] + of false: z: seq[int] + + proc `=destroy`(x: MyObj) = + if x.kind and x.y != nil: + deallocShared(x.y) + x.y = nil + ``` + Refactor into: + ```nim + type + MySubObj = object + val: ptr UncheckedArray[float] + MyObj = object + case kind: bool + of true: y: MySubObj + of false: z: seq[int] + + proc `=destroy`(x: MySubObj) = + if x.val != nil: + deallocShared(x.val) + x.val = nil + ``` ## Compiler changes diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 47cf83715..5e23d2284 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -1048,3 +1048,7 @@ proc listSymbolNames*(symbols: openArray[PSym]): string = result.add ", " result.add sym.name.s +proc isDiscriminantField*(n: PNode): bool = + if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags + elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags + else: false diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index bb9d30b8f..e2bcf3a74 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1489,15 +1489,6 @@ proc genPragma(p: BProc, n: PNode) = p.module.injectStmt = p.s(cpsStmts) else: discard -proc fieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = - if optFieldCheck in p.options: - var le = asgn[0] - if le.kind == nkCheckedFieldExpr: - var field = le[0][1].sym - result = sfDiscriminant in field.flags - elif le.kind == nkDotExpr: - var field = le[1].sym - result = sfDiscriminant in field.flags proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, field: PSym) = @@ -1534,24 +1525,20 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) = initLocExpr(p, e[0], a) getTemp(p, a.t, tmp) expr(p, e[1], tmp) - let field = dotExpr[1].sym - if optTinyRtti 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, - "if ($1 != $2) { #raiseObjectCaseTransition(); $3}$n", - [rdLoc(oldVal), rdLoc(newVal), raiseInstr(p)]) - else: + if optTinyRtti notin p.config.globalOptions: + let field = dotExpr[1].sym genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field) + message(p.config, e.info, warnCaseTransition) genAssignment(p, a, tmp, {}) proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = if e[0].kind == nkSym and sfGoto in e[0].sym.flags: genLineDir(p, e) genGotoVar(p, e[1]) - elif not fieldDiscriminantCheckNeeded(p, e): + elif optFieldCheck in p.options and isDiscriminantField(e[0]): + genLineDir(p, e) + asgnFieldDiscriminant(p, e) + else: let le = e[0] let ri = e[1] var a: TLoc @@ -1565,10 +1552,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = assert(a.t != nil) genLineDir(p, ri) loadInto(p, le, ri, a) - else: - genLineDir(p, e) - asgnFieldDiscriminant(p, e) - message(p.config, e.info, warnCaseTransition) proc genStmts(p: BProc, t: PNode) = var a: TLoc diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 29199aa41..07ab2730d 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -22,9 +22,9 @@ # - eliminate 'wasMoved(x); destroy(x)' pairs as a post processing step. import - intsets, ast, msgs, renderer, magicsys, types, idents, + intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, strutils, options, dfa, lowerings, tables, modulegraphs, msgs, - lineinfos, parampatterns, sighashes + lineinfos, parampatterns, sighashes, liftdestructors from trees import exprStructuralEquivalent from algorithm import reverse @@ -49,6 +49,12 @@ type uninit: IntSet # set of uninit'ed vars uninitComputed: bool + ProcessMode = enum + normal + consumed + sinkArg + + const toDebug {.strdefine.} = "" template dbg(body) = @@ -56,6 +62,9 @@ template dbg(body) = if c.owner.name.s == toDebug or toDebug == "always": body +proc p(n: PNode; c: var Con; mode: ProcessMode): PNode +proc moveOrCopy(dest, ri: PNode; c: var Con): PNode + proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int = var pc = pc while pc < c.g.len: @@ -220,16 +229,19 @@ proc makePtrType(c: Con, baseType: PType): PType = result = newType(tyPtr, c.owner) addSonSkipIntLit(result, baseType) +proc genOp(c: Con; op: PSym; dest: PNode): PNode = + let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) + addrExp.add(dest) + result = newTree(nkCall, newSymNode(op), addrExp) + proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = var op = t.attachedOps[kind] - if op == nil or op.ast[genericParamsPos].kind != nkEmpty: # give up and find the canonical type instead: let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.graph.canonTypes.getOrDefault(h) if canon != nil: op = canon.attachedOps[kind] - if op == nil: #echo dest.typ.id globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & @@ -241,9 +253,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = if kind == attachedDestructor: echo "destructor is ", op.id, " ", op.ast if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind]) - let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) - addrExp.add(dest) - result = newTree(nkCall, newSymNode(op), addrExp) + genOp(c, op, dest) proc genDestroy(c: Con; dest: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) @@ -300,6 +310,44 @@ proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = sym.typ = typ result = newSymNode(sym) +proc genDiscriminantAsgn(c: var Con; n: PNode): PNode = + # discriminator is ordinal value that doesn't need sink destroy + # but fields within active case branch might need destruction + + # tmp to support self assignments + let tmp = getTemp(c, n[1].typ, n.info) + c.addTopVar(tmp) + + result = newTree(nkStmtList) + result.add newTree(nkFastAsgn, tmp, p(n[1], c, consumed)) + result.add p(n[0], c, normal) + + let le = p(n[0], c, normal) + let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le + let objType = leDotExpr[0].typ + + if hasDestructor(objType): + if objType.attachedOps[attachedDestructor] != nil and + sfOverriden in objType.attachedOps[attachedDestructor].flags: + localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for object's with user defined destructor is not supported, object must have default destructor. +It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""") + result.add newTree(nkFastAsgn, le, tmp) + return + + # generate: if le != tmp: `=destroy`(le) + let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info) + let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) + cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info)) + cond.add le + cond.add tmp + let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) + notExpr.add newSymNode(createMagic(c.graph, "not", mNot)) + notExpr.add cond + result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, genOp(c, branchDestructor, le))) + result.add newTree(nkFastAsgn, le, tmp) + else: + result.add newTree(nkFastAsgn, le, tmp) + proc genWasMoved(n: PNode; c: var Con): PNode = result = newNodeI(nkCall, n.info) result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) @@ -338,15 +386,6 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) -type - ProcessMode = enum - normal - consumed - sinkArg - -proc p(n: PNode; c: var Con; mode: ProcessMode): PNode -proc moveOrCopy(dest, ri: PNode; c: var Con): PNode - proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':' proc passCopyToSink(n: PNode; c: var Con): PNode = @@ -867,6 +906,8 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode = cycleCheck(n, c) assert n[1].kind notin {nkAsgn, nkFastAsgn} result = moveOrCopy(p(n[0], c, mode), n[1], c) + elif isDiscriminantField(n[0]): + result = genDiscriminantAsgn(c, n) else: result = copyNode(n) result.add p(n[0], c, mode) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 9e19b9bc2..a64857765 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -26,6 +26,8 @@ type fn: PSym asgnForType: PType recurse: bool + filterDiscriminator: PSym # we generating destructor for case branch + addMemReset: bool # add wasMoved() call after destructor call c: PContext # c can be nil, then we are called from lambdalifting! proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) @@ -72,14 +74,62 @@ proc genAddr(g: ModuleGraph; x: PNode): PNode = result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ)) result.add x -proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode = - result = newNodeIT(nkCall, x.info, op.typ[0]) - result.add(newSymNode(op)) - result.add genAddr(g, x) + +proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = + result = newNodeI(nkCall, i.info) + result.add createMagic(g, name, magic).newSymNode + result.add i + +proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = + result = newNodeI(nkWhileStmt, c.info, 2) + let cmp = genBuiltin(c.g, mLtI, "<", i) + cmp.add genLen(c.g, dest) + cmp.typ = getSysType(c.g, c.info, tyBool) + result[0] = cmp + result[1] = newNodeI(nkStmtList, c.info) + +proc genIf(c: var TLiftCtx; cond, action: PNode): PNode = + result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action)) + +proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode = + # generate: cast[ptr ObjType](cast[int](addr(x)) - offsetOf(objType.field)) + let intType = getSysType(c.g, unknownLineInfo, tyInt) + + let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ)) + addrOf.add newDeref(newSymNode(x)) + let castExpr1 = newNodeIT(nkCast, c.info, intType) + castExpr1.add newNodeIT(nkType, c.info, intType) + castExpr1.add addrOf + + let dotExpr = newNodeIT(nkDotExpr, c.info, x.typ) + dotExpr.add newNodeIT(nkType, c.info, objType) + dotExpr.add newSymNode(field) + + let offsetOf = genBuiltin(c.g, mOffsetOf, "offsetof", dotExpr) + offsetOf.typ = intType + + let minusExpr = genBuiltin(c.g, mSubI, "-", castExpr1) + minusExpr.typ = intType + minusExpr.add offsetOf + + let objPtr = makePtrType(objType.owner, objType) + result = newNodeIT(nkCast, c.info, objPtr) + result.add newNodeIT(nkType, c.info, objPtr) + result.add minusExpr + +proc destructorCall(c: TLiftCtx; op: PSym; x: PNode): PNode = + var destroy = newNodeIT(nkCall, x.info, op.typ[0]) + destroy.add(newSymNode(op)) + destroy.add genAddr(c.g, x) + if c.addMemReset: + result = newTree(nkStmtList, destroy, genBuiltin(c.g, mWasMoved, "wasMoved", x)) + else: + result = destroy proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = case n.kind of nkSym: + if c.filterDiscriminator != nil: return let f = n.sym let b = if c.kind == attachedTrace: y else: y.dotField(f) if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and @@ -90,6 +140,9 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) fillBody(c, f.typ, body, x.dotField(f), b) of nkNilLit: discard of nkRecCase: + let oldfilterDiscriminator = c.filterDiscriminator + if c.filterDiscriminator == n[0].sym: + c.filterDiscriminator = nil # we have found the case part, proceed as normal # XXX This is only correct for 'attachedSink'! var localEnforceDefaultOp = enforceDefaultOp if c.kind == attachedSink: @@ -121,6 +174,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) caseStmt.add(branch) if emptyBranches != n.len-1: body.add(caseStmt) + c.filterDiscriminator = oldfilterDiscriminator of nkRecList: for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) else: @@ -275,7 +329,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) = if op != nil: #markUsed(c.g.config, c.info, op, c.g.usageSym) onUse(c.info, op) - body.add destructorCall(c.g, op, x) + body.add destructorCall(c, op, x) elif useNoGc(c, t): internalError(c.g.config, c.info, "type-bound operator could not be resolved") @@ -293,7 +347,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = #markUsed(c.g.config, c.info, op, c.g.usageSym) onUse(c.info, op) - body.add destructorCall(c.g, op, x) + body.add destructorCall(c, op, x) result = true #result = addDestructorCall(c, t, body, x) of attachedAsgn, attachedSink, attachedTrace: @@ -318,22 +372,6 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = v.addVar(result, lowerings.newIntLit(c.g, body.info, first)) body.add v -proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = - result = newNodeI(nkCall, i.info) - result.add createMagic(g, name, magic).newSymNode - result.add i - -proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = - result = newNodeI(nkWhileStmt, c.info, 2) - let cmp = genBuiltin(c.g, mLtI, "<", i) - cmp.add genLen(c.g, dest) - cmp.typ = getSysType(c.g, c.info, tyBool) - result[0] = cmp - result[1] = newNodeI(nkStmtList, c.info) - -proc genIf(c: var TLiftCtx; cond, action: PNode): PNode = - result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action)) - proc addIncStmt(c: var TLiftCtx; body, i: PNode) = let incCall = genBuiltin(c.g, mInc, "inc", i) incCall.add lowerings.newIntLit(c.g, c.info, 1) @@ -382,7 +420,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil - moveCall.add destructorCall(c.g, t.destructor, x) + moveCall.add destructorCall(c, t.destructor, x) body.add moveCall of attachedDestructor: # destroy all elements: @@ -413,7 +451,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil - moveCall.add destructorCall(c.g, t.destructor, x) + moveCall.add destructorCall(c, t.destructor, x) body.add moveCall # alternatively we could do this: when false: @@ -421,7 +459,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newHookCall(c.g, t.asink, x, y) of attachedDestructor: doAssert t.destructor != nil - body.add destructorCall(c.g, t.destructor, x) + body.add destructorCall(c, t.destructor, x) of attachedTrace: body.add newHookCall(c.g, t.attachedOps[c.kind], x, y) of attachedDispose: @@ -435,7 +473,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil - moveCall.add destructorCall(c.g, t.destructor, x) + moveCall.add destructorCall(c, t.destructor, x) body.add moveCall of attachedDestructor, attachedDispose: body.add genBuiltin(c.g, mDestroy, "destroy", x) @@ -738,33 +776,14 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; typ.attachedOps[kind] = baseType.attachedOps[kind] result = typ.attachedOps[kind] -proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; +proc symPrototype(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = - if typ.kind == tyDistinct: - return produceSymDistinctType(g, c, typ, kind, info) - var a: TLiftCtx - a.info = info - a.g = g - a.kind = kind - a.c = c - let body = newNodeI(nkStmtList, info) let procname = getIdent(g.cache, AttachedOpToStr[kind]) - result = newSym(skProc, procname, typ.owner, info) - a.fn = result - a.asgnForType = typ - let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info) let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), result, info) - var d: PNode - #if kind notin {attachedTrace, attachedDispose}: dest.typ = makeVarType(typ.owner, typ) - d = newDeref(newSymNode(dest)) - #else: - # dest.typ = typ - # d = newSymNode(dest) - if kind == attachedTrace: src.typ = getSysType(g, info, tyPointer) else: @@ -775,6 +794,30 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; if kind notin {attachedDestructor, attachedDispose}: result.typ.addParam src + var n = newNodeI(nkProcDef, info, bodyPos+1) + for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info) + n[namePos] = newSymNode(result) + n[paramsPos] = result.typ.n + n[bodyPos] = newNodeI(nkStmtList, info) + result.ast = n + incl result.flags, sfFromGeneric + incl result.flags, sfGeneratedOp + + +proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; + info: TLineInfo): PSym = + if typ.kind == tyDistinct: + return produceSymDistinctType(g, c, typ, kind, info) + + result = symPrototype(g, typ, kind, info) + var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType:typ) + a.fn = result + + let dest = result.typ.n[1].sym + let d = newDeref(newSymNode(dest)) + let src = if kind in {attachedDestructor, attachedDispose}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + else: newSymNode(result.typ.n[2].sym) + # register this operation already: typ.attachedOps[kind] = result @@ -782,8 +825,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; sfOverriden in typ.attachedOps[attachedDestructor].flags: ## compiler can use a combination of `=destroy` and memCopy for sink op dest.flags.incl sfCursor - body.add newOpCall(typ.attachedOps[attachedDestructor], d[0]) - body.add newAsgnStmt(d, newSymNode(src)) + result.ast[bodyPos].add newOpCall(typ.attachedOps[attachedDestructor], d[0]) + result.ast[bodyPos].add newAsgnStmt(d, src) else: var tk: TTypeKind if g.config.selectedGC in {gcArc, gcOrc, gcHooks}: @@ -792,20 +835,33 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; tk = tyNone # no special casing for strings and seqs case tk of tySequence: - fillSeqOp(a, typ, body, d, newSymNode(src)) + fillSeqOp(a, typ, result.ast[bodyPos], d, src) of tyString: - fillStrOp(a, typ, body, d, newSymNode(src)) + fillStrOp(a, typ, result.ast[bodyPos], d, src) else: - fillBody(a, typ, body, d, newSymNode(src)) + fillBody(a, typ, result.ast[bodyPos], d, src) + + +proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, info: TLineInfo): PSym = + assert(typ.kind == tyObject) + result = symPrototype(g, field.typ, attachedDestructor, info) + var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ) + a.fn = result + a.asgnForType = typ + a.filterDiscriminator = field + a.addMemReset = true + let discrimantDest = result.typ.n[1].sym + + let dst = newSym(skVar, getIdent(g.cache, "dest"), result, info) + dst.typ = makePtrType(typ.owner, typ) + let dstSym = newSymNode(dst) + let d = newDeref(dstSym) + let v = newNodeI(nkVarSection, info) + v.addVar(dstSym, genContainerOf(a, typ, field, discrimantDest)) + result.ast[bodyPos].add v + let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + fillBody(a, typ, result.ast[bodyPos], d, placeHolder) - var n = newNodeI(nkProcDef, info, bodyPos+1) - for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info) - n[namePos] = newSymNode(result) - n[paramsPos] = result.typ.n - n[bodyPos] = body - result.ast = n - incl result.flags, sfFromGeneric - incl result.flags, sfGeneratedOp template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = discard "now a nop" diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index bc3d7434b..604c5b537 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -177,3 +177,28 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym = strTableGet(g.exposed, getIdent(g.cache, name)) proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed) + +proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = + case t.kind + of tyInt, tyInt8, tyInt16, tyInt32, tyInt64, + tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64: + result = getSysMagic(g, info, "==", mEqI) + of tyEnum: + result = getSysMagic(g, info, "==", mEqEnum) + of tyBool: + result = getSysMagic(g, info, "==", mEqB) + of tyRef, tyPtr, tyPointer: + result = getSysMagic(g, info, "==", mEqRef) + of tyString: + result = getSysMagic(g, info, "==", mEqStr) + of tyChar: + result = getSysMagic(g, info, "==", mEqCh) + of tySet: + result = getSysMagic(g, info, "==", mEqSet) + of tyProc: + result = getSysMagic(g, info, "==", mEqProc) + else: + globalError(g.config, info, + "can't find magic equals operator for type kind " & $t.kind) + + diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4d836d8f2..278384fc4 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -278,10 +278,13 @@ proc addToLib*(lib: PLib, sym: PSym) = proc newTypeS*(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner(c)) -proc makePtrType*(c: PContext, baseType: PType): PType = - result = newTypeS(tyPtr, c) +proc makePtrType*(owner: PSym, baseType: PType): PType = + result = newType(tyPtr, owner) addSonSkipIntLit(result, baseType) +proc makePtrType*(c: PContext, baseType: PType): PType = + makePtrType(getCurrOwner(c), baseType) + proc makeTypeWithModifier*(c: PContext, modifier: TTypeKind, baseType: PType): PType = diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index 44daa5979..71f3960ef 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -8,6 +8,7 @@ A B begin end +prevented myobj destroyed ''' """ @@ -144,3 +145,51 @@ when true: let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})]) echo "end" testSubObjAssignment() + + +#------------------------------------------------ + +type + MyObject = object + x1: string + case kind1: bool + of false: y1: string + of true: + y2: seq[string] + case kind2: bool + of true: z1: string + of false: + z2: seq[string] + flag: bool + x2: string + +proc test_myobject = + var x: MyObject + x.x1 = "x1" + x.x2 = "x2" + x.y1 = "ljhkjhkjh" + x.kind1 = true + x.y2 = @["1", "2"] + x.kind2 = true + x.z1 = "yes" + x.kind2 = false + x.z2 = @["1", "2"] + x.kind2 = true + x.z1 = "yes" + x.kind2 = true # should be no effect + doAssert(x.z1 == "yes") + x.kind2 = false + x.kind1 = x.kind2 # support self assignment with effect + + try: + x.kind1 = x.flag # flag is not accesible + except FieldError: + echo "prevented" + + doAssert(x.x1 == "x1") + doAssert(x.x2 == "x2") + + +test_myobject() + + diff --git a/tests/destructor/tgotoexceptions7.nim b/tests/destructor/tgotoexceptions7.nim index 30fe28e7c..0f88caaa4 100644 --- a/tests/destructor/tgotoexceptions7.nim +++ b/tests/destructor/tgotoexceptions7.nim @@ -1,7 +1,6 @@ discard """ cmd: "nim c --gc:arc --exceptions:goto --panics:off $file" - output: '''field error prevented -prevented! + output: '''prevented! caught AssertionError 900''' @@ -26,11 +25,8 @@ proc helper = doAssert(false) proc main(i: int) = var obj = Obj(kind: kindA, s: "abc") - try: - obj.kind = kindB - except FieldError: - echo "field error prevented" - + obj.kind = kindB + obj.i = 2 try: var objA = ObjA() bplease(ObjB(objA)) |