diff options
-rw-r--r-- | compiler/ast.nim | 7 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 10 | ||||
-rw-r--r-- | compiler/condsyms.nim | 3 | ||||
-rw-r--r-- | compiler/injectdestructors.nim | 27 | ||||
-rw-r--r-- | compiler/jsgen.nim | 9 | ||||
-rw-r--r-- | compiler/liftdestructors.nim | 26 | ||||
-rw-r--r-- | compiler/semstmts.nim | 16 | ||||
-rw-r--r-- | compiler/vmgen.nim | 6 | ||||
-rw-r--r-- | lib/system.nim | 6 | ||||
-rw-r--r-- | lib/system/arc.nim | 4 | ||||
-rw-r--r-- | tests/arc/tdup.nim | 70 | ||||
-rw-r--r-- | tests/arc/topt_no_cursor.nim | 3 |
12 files changed, 170 insertions, 17 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index bf942f784..815cb00dc 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -687,7 +687,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDestroy, mTrace, + mMove, mWasMoved, mDup, mDestroy, mTrace, mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -944,7 +944,8 @@ type attachedAsgn, attachedSink, attachedTrace, - attachedDeepCopy + attachedDeepCopy, + attachedDup TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the @@ -1515,7 +1516,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, const AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] + "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy", "=dup"] proc `$`*(s: PSym): string = if s != nil: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 265ccb92c..e351e95b0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2346,6 +2346,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = genAssignment(p, d, a, {}) resetLoc(p, a) +proc genDup(p: BProc; src: TLoc; d: var TLoc; n: PNode) = + if d.k == locNone: getTemp(p, n.typ, d) + linefmt(p, cpsStmts, "#nimDupRef((void**)$1, (void*)$2);$n", + [addrLoc(p.config, d), rdLoc(src)]) + proc genDestroy(p: BProc; n: PNode) = if optSeqDestructors in p.config.globalOptions: let arg = n[1].skipAddr @@ -2597,6 +2602,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) of mTrace: discard "no code to generate" + of mDup: + var a: TLoc + let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] + initLocExpr(p, x, a) + genDup(p, a, d, e) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index fa7f56504..8e0e2f300 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -154,5 +154,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasGenericDefine") defineSymbol("nimHasDefineAliases") defineSymbol("nimHasWarnBareExcept") - + defineSymbol("nimHasDup") defineSymbol("nimHasChecksums") + diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ccb19720d..d9a2da5a0 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -425,11 +425,28 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) let tmp = c.getTemp(s, n.typ, n.info) if hasDestructor(c, n.typ): - result.add c.genWasMoved(tmp) - var m = c.genCopy(tmp, n, {}) - m.add p(n, c, s, normal) - c.finishCopy(m, n, isFromSink = true) - result.add m + let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + let op = getAttachedOp(c.graph, typ, attachedDup) + if op != nil: + let src = p(n, c, s, normal) + result.add newTreeI(nkFastAsgn, + src.info, tmp, + genOp(c, op, src) + ) + elif typ.kind == tyRef: + let src = p(n, c, s, normal) + result.add newTreeI(nkFastAsgn, + src.info, tmp, + newTreeIT(nkCall, src.info, src.typ, + newSymNode(createMagic(c.graph, c.idgen, "`=dup`", mDup)), + src) + ) + else: + result.add c.genWasMoved(tmp) + var m = c.genCopy(tmp, n, {}) + m.add p(n, c, s, normal) + c.finishCopy(m, n, isFromSink = true) + result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 619555969..45b0baec0 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2174,6 +2174,13 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = genReset(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) +proc genDup(p: PProc; n: PNode; r: var TCompRes) = + var a: TCompRes + r.kind = resVal + r.res = p.getTemp() + gen(p, n[1], a) + lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) + proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("[") @@ -2368,6 +2375,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr of mMove: genMove(p, n, r) + of mDup: + genDup(p, n, r) else: genCall(p, n, r) #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 3fd3c5f37..3a997af82 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -463,6 +463,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add genWasMovedCall(c, op, x) result = true + of attachedDup: + assert false, "cannot happen" + proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info) temp.typ = getSysType(c.g, body.info, tyInt) @@ -546,6 +549,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # follow all elements: forallElements(c, t, body, x, y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -584,6 +589,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = return # protect from recursion body.add newHookCall(c, op, x, y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -600,6 +607,8 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc cyclicType*(t: PType): bool = case t.kind @@ -699,7 +708,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) #echo "can follow ", elemType, " static ", isFinal(elemType) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) - + of attachedDup: + assert false, "cannot happen" proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor @@ -749,6 +759,8 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -774,6 +786,8 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) @@ -800,6 +814,8 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDeepCopy: @@ -835,6 +851,8 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) @@ -851,6 +869,8 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind @@ -966,7 +986,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest - if kind notin {attachedDestructor, attachedWasMoved}: + if kind notin {attachedDestructor, attachedWasMoved, attachedDup}: result.typ.addParam src if kind == attachedAsgn and g.config.selectedGC == gcOrc and @@ -1006,7 +1026,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; let dest = result.typ.n[1].sym let d = newDeref(newSymNode(dest)) - let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + let src = if kind in {attachedDestructor, attachedWasMoved, attachedDup}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) else: newSymNode(result.typ.n[2].sym) # register this operation already: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e6fade528..126d1aa65 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1815,9 +1815,12 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType = proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false - let cond = if op in {attachedDestructor, attachedWasMoved}: + let cond = case op + of {attachedDestructor, attachedWasMoved}: t.len == 2 and t[0] == nil and t[1].kind == tyVar - elif op == attachedTrace: + of attachedDup: + t.len == 2 and t[0] != nil and t[1].kind == tyVar + of attachedTrace: t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer else: t.len >= 2 and t[0] == nil @@ -1843,9 +1846,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = localError(c.config, n.info, errGenerated, "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: - if op == attachedTrace: + case op + of attachedTrace: localError(c.config, n.info, errGenerated, "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") + of attachedDup: + localError(c.config, n.info, errGenerated, + "signature for '=dup' must be proc[T: object](x: var T): T") else: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") @@ -1938,6 +1945,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=wasmoved": if s.magic != mWasMoved: bindTypeHook(c, s, n, attachedWasMoved) + of "=dup": + if s.magic != mDup: + bindTypeHook(c, s, n, attachedDup) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index db3276aad..25ff62bcc 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1401,6 +1401,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # c.gABx(n, opcNodeToReg, a, a) # c.genAsgnPatch(arg, a) c.freeTemp(a) + of mDup: + let arg = n[1] + let a = c.genx(arg) + if dest < 0: dest = c.getTemp(arg.typ) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) + c.freeTemp(a) of mNodeId: c.genUnaryABC(n, dest, opcNodeId) else: diff --git a/lib/system.nim b/lib/system.nim index f23242315..8bb50ba5d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -347,6 +347,12 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I; proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## Generic `destructor`:idx: implementation that can be overridden. discard + +when defined(nimHasDup): + proc `=dup`*[T](x: ref T): ref T {.inline, magic: "Dup".} = + ## Generic `dup` implementation that can be overridden. + discard + proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} = ## Generic `sink`:idx: implementation that can be overridden. when defined(gcArc) or defined(gcOrc): diff --git a/lib/system/arc.nim b/lib/system/arc.nim index d8527e1e4..55c4c412a 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -185,6 +185,10 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = when traceCollector: cprintf("[DeCREF] %p\n", cell) +proc nimDupRef(dest: ptr pointer, src: pointer) {.compilerRtl, inl.} = + dest[] = src + if src != nil: nimIncRef src + proc GC_unref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. var y {.cursor.} = x diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim new file mode 100644 index 000000000..3f64061fb --- /dev/null +++ b/tests/arc/tdup.nim @@ -0,0 +1,70 @@ +discard """ + cmd: "nim c --mm:arc --expandArc:foo --hints:off $file" + nimout: ''' +--expandArc: foo + +var + x + :tmpD + s + :tmpD_1 +x = Ref(id: 8) +inc: + :tmpD = `=dup`(x) + :tmpD +inc: + let blitTmp = x + blitTmp +var id_1 = 777 +s = RefCustom(id_2: addr(id_1)) +inc_1 : + :tmpD_1 = `=dup`(s) + :tmpD_1 +inc_1 : + let blitTmp_1 = s + blitTmp_1 +-- end of expandArc ------------------------ +''' +""" + +type + Ref = ref object + id: int + + RefCustom = object + id: ptr int + +proc inc(x: sink Ref) = + inc x.id + +proc inc(x: sink RefCustom) = + inc x.id[] + +proc `=dup`(x: var RefCustom): RefCustom = + result.id = x.id + +proc foo = + var x = Ref(id: 8) + inc(x) + inc(x) + var id = 777 + var s = RefCustom(id: addr id) + inc s + inc s + +foo() + +proc foo2 = + var x = Ref(id: 8) + inc(x) + doAssert x.id == 9 + inc(x) + doAssert x.id == 10 + var id = 777 + var s = RefCustom(id: addr id) + inc s + doAssert s.id[] == 778 + inc s + doAssert s.id[] == 779 + +foo2() diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 26dc25447..32652b60a 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -113,8 +113,7 @@ block :tmp: var :tmpD sym = shadowScope.symbols[i] addInterfaceDecl(c): - `=wasMoved`(:tmpD) - `=copy_1`(:tmpD, sym) + :tmpD = `=dup`(sym) :tmpD inc(i, 1) `=destroy`(shadowScope) |