* fixes #20572

* added a test case
This commit is contained in:
Andreas Rumpf
2022-10-17 23:48:51 +02:00
committed by GitHub
parent c0824b9b80
commit 81087c949f
25 changed files with 116 additions and 70 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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.

View File

@@ -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

View File

@@ -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..<n.len:
n[i] = ctx.lowerStmtListExprs(n[i], ns)
@@ -868,7 +868,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
if n[0].kind != nkEmpty:
let asgnTmpResult = newNodeI(nkAsgn, n.info)
asgnTmpResult.add(ctx.newTmpResultAccess())
let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
let x = if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: n[0][1] else: n[0]
asgnTmpResult.add(x)
result.add(asgnTmpResult)

View File

@@ -433,7 +433,7 @@ proc gen(c: var Con; n: PNode) =
else:
genCall(c, n)
of nkCharLit..nkNilLit: discard
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
gen(c, n[1])
if n[0].kind in PathKinds0:

View File

@@ -400,7 +400,7 @@ proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
result = genComment(d, n)
if result == nil:
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkHiddenStdConv}:
nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}:
# notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
for i in 0..<n.len:
result = genRecCommentAux(d, n[i])
@@ -1551,7 +1551,7 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
else:
# Just have the link
d.toc[kind] = getConfigVar(d.conf, "doc.section.toc_item") % sectionValues
proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string =
$relativeTo(outDir / linkto, destFile.splitFile().dir, '/')

View File

@@ -71,7 +71,7 @@ proc hlo(c: PContext, n: PNode): PNode =
# already processed (special cases in semstmts.nim)
result = n
else:
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
n[0].kind == nkSym and
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again!

View File

@@ -78,7 +78,12 @@ proc nestedScope(parent: var Scope; body: PNode): Scope =
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode
type
MoveOrCopyFlag = enum
IsDecl, IsExplicitSink
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
when false:
var
@@ -243,8 +248,8 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
proc isNoInit(dest: PNode): bool {.inline.} =
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or
proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
isNoInit(dest):
# optimize sink call into a bitwise memcopy
@@ -296,17 +301,21 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode =
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
result = c.genOp(t, attachedAsgn, dest, ri)
result = c.genOp(t, a, dest, ri)
assert ri.typ != nil
proc genCopy(c: var Con; dest, ri: PNode): PNode =
proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
let t = dest.typ
if tfHasOwned in t.flags and ri.kind != nkNilLit:
# try to improve the error message here:
c.checkForErrorPragma(t, ri, "=copy")
result = c.genCopyNoCheck(dest, ri)
if IsExplicitSink in flags:
c.checkForErrorPragma(t, ri, "=sink")
else:
c.checkForErrorPragma(t, ri, "=copy")
let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
result = c.genCopyNoCheck(dest, ri, a)
assert ri.typ != nil
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
@@ -391,7 +400,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
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)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, isFromSink = true)
result.add m
@@ -436,7 +445,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(tmp, arg, isDecl = true)
result.add c.genSink(tmp, arg, {IsDecl})
result.add tmp
s.final.add c.genDestroy(tmp)
else:
@@ -518,7 +527,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
tmp.sym.flags = tmpFlags
let cpy = if hasDestructor(c, ret.typ):
s.parent[].final.add c.genDestroy(tmp)
moveOrCopy(tmp, ret, c, s, isDecl = true)
moveOrCopy(tmp, ret, c, s, {IsDecl})
else:
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
@@ -654,7 +663,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result.add call
else:
let tmp = c.getTemp(s, n[0].typ, n.info)
var m = c.genCopyNoCheck(tmp, n[0])
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
m.add p(n[0], c, s, normal)
c.finishCopy(m, n[0], isFromSink = false)
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
@@ -818,9 +827,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
s.locals.add v.sym
pVarTopLevel(v, c, s, result)
if ri.kind != nkEmpty:
result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
elif ri.kind == nkEmpty and c.inLoop > 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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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])

View File

@@ -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.} =

View File

@@ -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, "(")

View File

@@ -306,7 +306,7 @@ proc semGenericStmt(c: PContext, n: PNode,
result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
for i in 0..<n.len: result.add(n[i])
result = semGenericStmt(c, result, flags, ctx)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
checkSonsLen(n, 2, c.config)
let a = n[0]
let b = n[1]

View File

@@ -78,8 +78,8 @@ proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
result[1] = n.lastSon
result = semAsgn(c, result, noOverloadedSubscript)
proc semAsgnOpr(c: PContext; n: PNode): PNode =
result = newNodeI(nkAsgn, n.info, 2)
proc semAsgnOpr(c: PContext; n: PNode; k: TNodeKind): PNode =
result = newNodeI(k, n.info, 2)
result[0] = n[1]
result[1] = n[2]
result = semAsgn(c, result, noOverloadedAsgn)
@@ -553,7 +553,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = semArrPut(c, n, flags)
of mAsgn:
if n[0].sym.name.s == "=":
result = semAsgnOpr(c, n)
result = semAsgnOpr(c, n, nkAsgn)
elif n[0].sym.name.s == "=sink":
result = semAsgnOpr(c, n, nkSinkAsgn)
else:
result = semShallowCopy(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags)

View File

@@ -323,7 +323,7 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
proc analyse(c: var AnalysisCtx; n: PNode) =
case n.kind
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
let y = n[1].skipConv
if n[0].isSingleAssignable and y.isLocal:
let slot = c.getSlot(y.sym)
@@ -442,7 +442,7 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier:
else:
it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
if result.isNil: result = n
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
let b = n[1]
if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
spawnResult(t, true) == srByVar):

View File

@@ -1053,7 +1053,7 @@ proc track(tracked: PEffects, n: PNode) =
checkFieldAccess(tracked.guards, n, tracked.config)
of nkTryStmt: trackTryStmt(tracked, n)
of nkPragma: trackPragmaStmt(tracked, n)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
track(tracked, n[1])
initVar(tracked, n[0], volatileCheck=true)
invalidateFacts(tracked.guards, n[0])

View File

@@ -511,7 +511,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
for i in 0..<n.len: result.add(n[i])
result = semTemplBodySons(c, result)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
checkSonsLen(n, 2, c.c.config)
let a = n[0]
let b = n[1]

View File

@@ -53,7 +53,7 @@ proc findDocComment(n: PNode): PNode =
if result != nil: return
if n.len > 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 =

View File

@@ -686,7 +686,7 @@ proc traverse(c: var Partitions; n: PNode) =
for i in 0..<child.len-2:
#registerVariable(c, child[i])
deps(c, child[i], last)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
traverse(c, n[0])
inc c.inAsgnSource
traverse(c, n[1])
@@ -779,7 +779,7 @@ proc traverse(c: var Partitions; n: PNode) =
if n.kind == nkWhileStmt:
traverse(c, n[0])
# variables in while condition has longer alive time than local variables
# variables in while condition has longer alive time than local variables
# in the while loop body
else:
for child in n: traverse(c, child)
@@ -811,7 +811,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
registerVariable(c, child[i])
#deps(c, child[i], last)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
computeLiveRanges(c, n[0])
computeLiveRanges(c, n[1])
if n[0].kind == nkSym:
@@ -871,7 +871,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
if n.kind == nkWhileStmt:
computeLiveRanges(c, n[0])
# variables in while condition has longer alive time than local variables
# variables in while condition has longer alive time than local variables
# in the while loop body
of nkElifBranch, nkElifExpr, nkElse, nkOfBranch:
inc c.inConditional

View File

@@ -2045,7 +2045,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkNilLit:
if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
else: unused(c, n, dest)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn, nkSinkAsgn:
unused(c, n, dest)
genAsgn(c, n[0], n[1], n.kind == nkAsgn)
of nkDotExpr: genObjAccess(c, n, dest, flags)

View File

@@ -79,7 +79,7 @@ type
nnkDistinctTy,
nnkProcTy,
nnkIteratorTy, # iterator type
nnkSharedTy, # 'shared T'
nnkSinkAsgn,
nnkEnumTy,
nnkEnumFieldDef,
nnkArgList, nnkPattern
@@ -127,6 +127,7 @@ type
const
nnkMutableTy* {.deprecated.} = nnkOutTy
nnkSharedTy* {.deprecated.} = nnkSinkAsgn
type
NimIdent* {.deprecated.} = object of RootObj

View File

@@ -0,0 +1,29 @@
discard """
output: '''de'''
cmd: '''nim c --mm:arc --expandArc:main $file'''
nimout: '''--expandArc: main
var
a
b_cursor
try:
a = f "abc"
b_cursor = "de"
`=sink`(a, b_cursor)
echo [a]
finally:
`=destroy`(a)
-- end of expandArc ------------------------'''
"""
# bug #20572
proc f(s: string): string = s
proc main =
var a = f "abc"
var b = "de"
`=sink`(a, b)
echo a
main()