From 81087c949f620dc80697364da414872791ffe23c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 17 Oct 2022 23:48:51 +0200 Subject: [PATCH] fixes #20572 (#20585) * fixes #20572 * added a test case --- compiler/ast.nim | 3 +- compiler/ccgcalls.nim | 2 +- compiler/ccgexprs.nim | 2 +- compiler/cgen.nim | 4 +- compiler/closureiters.nim | 4 +- compiler/dfa.nim | 2 +- compiler/docgen.nim | 4 +- compiler/hlo.nim | 2 +- compiler/injectdestructors.nim | 84 +++++++++++++++++++--------------- compiler/jsgen.nim | 2 +- compiler/lambdalifting.nim | 2 +- compiler/nilcheck.nim | 2 +- compiler/optimizer.nim | 2 +- compiler/patterns.nim | 2 +- compiler/renderer.nim | 7 ++- compiler/semgnrc.nim | 2 +- compiler/semmagic.nim | 8 ++-- compiler/semparallel.nim | 4 +- compiler/sempass2.nim | 2 +- compiler/semtempl.nim | 2 +- compiler/suggest.nim | 2 +- compiler/varpartitions.nim | 8 ++-- compiler/vmgen.nim | 2 +- lib/core/macros.nim | 3 +- tests/arc/texplicit_sink.nim | 29 ++++++++++++ 25 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 tests/arc/texplicit_sink.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index c4c1839328..228f7381c9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -211,8 +211,7 @@ type nkDistinctTy, # distinct type nkProcTy, # proc type nkIteratorTy, # iterator type - nkSharedTy, # 'shared T' - # we use 'nkPostFix' for the 'not nil' addition + nkSinkAsgn, # '=sink(x, y)' nkEnumTy, # enum body nkEnumFieldDef, # `ident = expr` in an enumeration nkArgList, # argument list diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 81e74d089f..c9ab1ddcbc 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -335,7 +335,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = of nkLiterals, nkIdent, nkFormalParams: discard of nkSym: if mutate: result.add n - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: getPotentialWrites(n[0], true, result) getPotentialWrites(n[1], mutate, result) of nkAddr, nkHiddenAddr: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f71a4ddbd8..a34e0535c6 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3086,7 +3086,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = cow(p, n[1]) if nfPreventCg notin n.flags: genAsgn(p, n, fastAsgn=false) - of nkFastAsgn: + of nkFastAsgn, nkSinkAsgn: cow(p, n[1]) if nfPreventCg notin n.flags: # transf is overly aggressive with 'nkFastAsgn', so we work around here. diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e2b7c67ad2..5fc42bfec4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -926,7 +926,7 @@ proc easyResultAsgn(n: PNode): PNode = var i = 0 while i < n.len and n[i].kind in harmless: inc i if i < n.len: result = easyResultAsgn(n[i]) - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]): incl n.flags, nfPreventCg return n[1] @@ -968,7 +968,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = for it in n: result = allPathsAsgnResult(it) if result != Unknown: return result - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: if n[0].kind == nkSym and n[0].sym.kind == skResult: if not containsResult(n[1]): result = InitSkippable else: result = InitRequired diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 613fbe582c..79038a4f57 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -718,7 +718,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = n[^1] = ex result.add(n) - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: var ns = false for i in 0.. 0: - result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = v.kind == nkSym) + result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {}) else: # keep the var but transform 'ri': var v = copyNode(n) var itCopy = copyNode(it) @@ -833,12 +842,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing itCopy.add p(it[^1], c, s, normal, tmpFlags = flags) v.add itCopy result.add v - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}: if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: cycleCheck(n, c) - assert n[1].kind notin {nkAsgn, nkFastAsgn} - result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s) + assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn} + let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {} + result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags) elif isDiscriminantField(n[0]): result = c.genDiscriminantAsgn(s, n) else: @@ -963,7 +973,7 @@ proc sameLocation*(a, b: PNode): bool = of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b) else: false -proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode, isDecl: bool): PNode = +proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = # with side effects var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info) temp.typ = ri[1].typ @@ -980,17 +990,17 @@ proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode, isDecl: bool): PNode newAccess.add ri[0] newAccess.add tempAsNode - var snk = c.genSink(dest, newAccess, isDecl) + var snk = c.genSink(dest, newAccess, flags) result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess)) -proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode = +proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode = if sameLocation(dest, ri): # rule (self-assignment-removal): result = newNodeI(nkEmpty, dest.info) elif isCursor(dest): case ri.kind: of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) # We know the result will be a stmt so we use that fact to optimize handleNestedTempl(ri, process, willProduceStmt = true) else: @@ -998,53 +1008,53 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod else: case ri.kind of nkCallKinds: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(dest, p(ri, c, s, consumed), flags) of nkBracketExpr: if isUnpackedTuple(ri[0]): # unpacking of tuple: take over the elements - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(dest, p(ri, c, s, consumed), flags) elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s): if aliases(dest, ri) == no: # Rule 3: `=sink`(x, z); wasMoved(z) if isAtom(ri[1]): - var snk = c.genSink(dest, ri, isDecl) + var snk = c.genSink(dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = genFieldAccessSideEffects(c, dest, ri, isDecl) + result = genFieldAccessSideEffects(c, dest, ri, flags) else: - result = c.genSink(dest, destructiveMoveVar(ri, c, s), isDecl) + result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags) else: - result = c.genCopy(dest, ri) + result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): - result = c.genCopy(dest, ri) + result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) else: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(dest, p(ri, c, s, consumed), flags) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(dest, p(ri, c, s, consumed), flags) of nkSym: if isSinkParam(ri.sym) and isLastRead(ri, c, s): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) + let snk = c.genSink(dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) + let snk = c.genSink(dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = c.genCopy(dest, ri) + result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: - result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) + result = c.genSink(dest, p(ri, c, s, sinkArg), flags) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) # We know the result will be a stmt so we use that fact to optimize handleNestedTempl(ri, process, willProduceStmt = true) of nkRaiseStmt: @@ -1053,10 +1063,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) + let snk = c.genSink(dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = c.genCopy(dest, ri) + result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 267b9ba34d..29bfb7df54 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2711,7 +2711,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: genAsgn(p, n) - of nkFastAsgn: genFastAsgn(p, n) + of nkFastAsgn, nkSinkAsgn: genFastAsgn(p, n) of nkDiscardStmt: if n[0].kind != nkEmpty: genLineDir(p, n) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ca9b29a778..a34f4ba948 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -782,7 +782,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; n[1] = liftCapturedVars(n[1], owner, d, c) if n[1].kind == nkClosure: result = n[1] of nkReturnStmt: - if n[0].kind in {nkAsgn, nkFastAsgn}: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: # we have a `result = result` expression produced by the closure # transform, let's not touch the LHS in order to make the lifting pass # correct when `result` is lifted diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 49ceb8942b..91d76b2b8d 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -1242,7 +1242,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = result = check(n.sons[0], ctx, map) of nkIfStmt, nkIfExpr: result = checkIf(n, ctx, map) - of nkAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: result = checkAsgn(n[0], n[1], ctx, map) of nkVarSection: result.map = map diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index 10b092e112..98939f80de 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -154,7 +154,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = nkTypeOfExpr, nkMixinStmt, nkBindStmt: discard "do not follow the construct" - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: # reverse order, see remark for `=sink`: analyse(c, b, n[1]) analyse(c, b, n[0]) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index d4577981f0..ff9a9efa34 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -44,7 +44,7 @@ proc canonKind(n: PNode): TNodeKind = case result of nkCallKinds: result = nkCall of nkStrLit..nkTripleStrLit: result = nkStrLit - of nkFastAsgn: result = nkAsgn + of nkFastAsgn, nkSinkAsgn: result = nkAsgn else: discard proc sameKinds(a, b: PNode): bool {.inline.} = diff --git a/compiler/renderer.nim b/compiler/renderer.nim index eb410bc5d8..f975dc8965 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -518,7 +518,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkOfInherit: result = lsub(g, n[0]) + len("of_") of nkProcTy: result = lsons(g, n) + len("proc_") of nkIteratorTy: result = lsons(g, n) + len("iterator_") - of nkSharedTy: result = lsons(g, n) + len("shared_") + of nkSinkAsgn: result = lsons(g, n) + len("`=sink`(, )") of nkEnumTy: if n.len > 0: result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_") @@ -1170,6 +1170,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n, 1) + of nkSinkAsgn: + put(g, tkSymbol, "`=sink`") + put(g, tkParLe, "(") + gcomma(g, n) + put(g, tkParRi, ")") of nkChckRangeF: put(g, tkSymbol, "chckRangeF") put(g, tkParLe, "(") diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index d1c4acef68..0827e68456 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -306,7 +306,7 @@ proc semGenericStmt(c: PContext, n: PNode, result.add newIdentNode(getIdent(c.cache, "[]"), n.info) for i in 0.. 1: result = findDocComment(n[1]) - elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2: + elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2: result = findDocComment(n[1]) proc extractDocComment(g: ModuleGraph; s: PSym): string = diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 60eed5c78b..c83da3277c 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -686,7 +686,7 @@ proc traverse(c: var Partitions; n: PNode) = for i in 0..