parent
c0824b9b80
commit
81087c949f
25 changed files with 116 additions and 70 deletions
|
|
@ -211,8 +211,7 @@ type
|
||||||
nkDistinctTy, # distinct type
|
nkDistinctTy, # distinct type
|
||||||
nkProcTy, # proc type
|
nkProcTy, # proc type
|
||||||
nkIteratorTy, # iterator type
|
nkIteratorTy, # iterator type
|
||||||
nkSharedTy, # 'shared T'
|
nkSinkAsgn, # '=sink(x, y)'
|
||||||
# we use 'nkPostFix' for the 'not nil' addition
|
|
||||||
nkEnumTy, # enum body
|
nkEnumTy, # enum body
|
||||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||||
nkArgList, # argument list
|
nkArgList, # argument list
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
|
||||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||||
of nkSym:
|
of nkSym:
|
||||||
if mutate: result.add n
|
if mutate: result.add n
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
getPotentialWrites(n[0], true, result)
|
getPotentialWrites(n[0], true, result)
|
||||||
getPotentialWrites(n[1], mutate, result)
|
getPotentialWrites(n[1], mutate, result)
|
||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
|
|
|
||||||
|
|
@ -3086,7 +3086,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
cow(p, n[1])
|
cow(p, n[1])
|
||||||
if nfPreventCg notin n.flags:
|
if nfPreventCg notin n.flags:
|
||||||
genAsgn(p, n, fastAsgn=false)
|
genAsgn(p, n, fastAsgn=false)
|
||||||
of nkFastAsgn:
|
of nkFastAsgn, nkSinkAsgn:
|
||||||
cow(p, n[1])
|
cow(p, n[1])
|
||||||
if nfPreventCg notin n.flags:
|
if nfPreventCg notin n.flags:
|
||||||
# transf is overly aggressive with 'nkFastAsgn', so we work around here.
|
# transf is overly aggressive with 'nkFastAsgn', so we work around here.
|
||||||
|
|
|
||||||
|
|
@ -926,7 +926,7 @@ proc easyResultAsgn(n: PNode): PNode =
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < n.len and n[i].kind in harmless: inc i
|
while i < n.len and n[i].kind in harmless: inc i
|
||||||
if i < n.len: result = easyResultAsgn(n[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]):
|
if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]):
|
||||||
incl n.flags, nfPreventCg
|
incl n.flags, nfPreventCg
|
||||||
return n[1]
|
return n[1]
|
||||||
|
|
@ -968,7 +968,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||||
for it in n:
|
for it in n:
|
||||||
result = allPathsAsgnResult(it)
|
result = allPathsAsgnResult(it)
|
||||||
if result != Unknown: return result
|
if result != Unknown: return result
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
||||||
if not containsResult(n[1]): result = InitSkippable
|
if not containsResult(n[1]): result = InitSkippable
|
||||||
else: result = InitRequired
|
else: result = InitRequired
|
||||||
|
|
|
||||||
|
|
@ -718,7 +718,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||||
n[^1] = ex
|
n[^1] = ex
|
||||||
result.add(n)
|
result.add(n)
|
||||||
|
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
var ns = false
|
var ns = false
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||||
|
|
@ -868,7 +868,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
||||||
asgnTmpResult.add(ctx.newTmpResultAccess())
|
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)
|
asgnTmpResult.add(x)
|
||||||
result.add(asgnTmpResult)
|
result.add(asgnTmpResult)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ proc gen(c: var Con; n: PNode) =
|
||||||
else:
|
else:
|
||||||
genCall(c, n)
|
genCall(c, n)
|
||||||
of nkCharLit..nkNilLit: discard
|
of nkCharLit..nkNilLit: discard
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
gen(c, n[1])
|
gen(c, n[1])
|
||||||
|
|
||||||
if n[0].kind in PathKinds0:
|
if n[0].kind in PathKinds0:
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
|
||||||
result = genComment(d, n)
|
result = genComment(d, n)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
|
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}:
|
# notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
result = genRecCommentAux(d, n[i])
|
result = genRecCommentAux(d, n[i])
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||||
# already processed (special cases in semstmts.nim)
|
# already processed (special cases in semstmts.nim)
|
||||||
result = n
|
result = n
|
||||||
else:
|
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
|
n[0].kind == nkSym and
|
||||||
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
||||||
# do not optimize 'var g {.global} = re(...)' again!
|
# do not optimize 'var g {.global} = re(...)' again!
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,12 @@ proc nestedScope(parent: var Scope; body: PNode): Scope =
|
||||||
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
|
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 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:
|
when false:
|
||||||
var
|
var
|
||||||
|
|
@ -243,8 +248,8 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
|
||||||
proc isNoInit(dest: PNode): bool {.inline.} =
|
proc isNoInit(dest: PNode): bool {.inline.} =
|
||||||
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
||||||
|
|
||||||
proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
|
proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or
|
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
||||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||||
isNoInit(dest):
|
isNoInit(dest):
|
||||||
# optimize sink call into a bitwise memcopy
|
# 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)
|
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
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})
|
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
|
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
|
let t = dest.typ
|
||||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||||
# try to improve the error message here:
|
# try to improve the error message here:
|
||||||
|
if IsExplicitSink in flags:
|
||||||
|
c.checkForErrorPragma(t, ri, "=sink")
|
||||||
|
else:
|
||||||
c.checkForErrorPragma(t, ri, "=copy")
|
c.checkForErrorPragma(t, ri, "=copy")
|
||||||
result = c.genCopyNoCheck(dest, ri)
|
let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
|
||||||
|
result = c.genCopyNoCheck(dest, ri, a)
|
||||||
assert ri.typ != nil
|
assert ri.typ != nil
|
||||||
|
|
||||||
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
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)
|
let tmp = c.getTemp(s, n.typ, n.info)
|
||||||
if hasDestructor(c, n.typ):
|
if hasDestructor(c, n.typ):
|
||||||
result.add c.genWasMoved(tmp)
|
result.add c.genWasMoved(tmp)
|
||||||
var m = c.genCopy(tmp, n)
|
var m = c.genCopy(tmp, n, {})
|
||||||
m.add p(n, c, s, normal)
|
m.add p(n, c, s, normal)
|
||||||
c.finishCopy(m, n, isFromSink = true)
|
c.finishCopy(m, n, isFromSink = true)
|
||||||
result.add m
|
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.
|
# This was already done in the sink parameter handling logic.
|
||||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||||
let tmp = c.getTemp(s, arg.typ, arg.info)
|
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
|
result.add tmp
|
||||||
s.final.add c.genDestroy(tmp)
|
s.final.add c.genDestroy(tmp)
|
||||||
else:
|
else:
|
||||||
|
|
@ -518,7 +527,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||||
tmp.sym.flags = tmpFlags
|
tmp.sym.flags = tmpFlags
|
||||||
let cpy = if hasDestructor(c, ret.typ):
|
let cpy = if hasDestructor(c, ret.typ):
|
||||||
s.parent[].final.add c.genDestroy(tmp)
|
s.parent[].final.add c.genDestroy(tmp)
|
||||||
moveOrCopy(tmp, ret, c, s, isDecl = true)
|
moveOrCopy(tmp, ret, c, s, {IsDecl})
|
||||||
else:
|
else:
|
||||||
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
|
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
|
result.add call
|
||||||
else:
|
else:
|
||||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
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)
|
m.add p(n[0], c, s, normal)
|
||||||
c.finishCopy(m, n[0], isFromSink = false)
|
c.finishCopy(m, n[0], isFromSink = false)
|
||||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
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
|
s.locals.add v.sym
|
||||||
pVarTopLevel(v, c, s, result)
|
pVarTopLevel(v, c, s, result)
|
||||||
if ri.kind != nkEmpty:
|
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:
|
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':
|
else: # keep the var but transform 'ri':
|
||||||
var v = copyNode(n)
|
var v = copyNode(n)
|
||||||
var itCopy = copyNode(it)
|
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)
|
itCopy.add p(it[^1], c, s, normal, tmpFlags = flags)
|
||||||
v.add itCopy
|
v.add itCopy
|
||||||
result.add v
|
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 hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
||||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||||
cycleCheck(n, c)
|
cycleCheck(n, c)
|
||||||
assert n[1].kind notin {nkAsgn, nkFastAsgn}
|
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
|
||||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s)
|
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]):
|
elif isDiscriminantField(n[0]):
|
||||||
result = c.genDiscriminantAsgn(s, n)
|
result = c.genDiscriminantAsgn(s, n)
|
||||||
else:
|
else:
|
||||||
|
|
@ -963,7 +973,7 @@ proc sameLocation*(a, b: PNode): bool =
|
||||||
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
||||||
else: false
|
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
|
# with side effects
|
||||||
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
|
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
|
||||||
temp.typ = ri[1].typ
|
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 ri[0]
|
||||||
newAccess.add tempAsNode
|
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))
|
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):
|
if sameLocation(dest, ri):
|
||||||
# rule (self-assignment-removal):
|
# rule (self-assignment-removal):
|
||||||
result = newNodeI(nkEmpty, dest.info)
|
result = newNodeI(nkEmpty, dest.info)
|
||||||
elif isCursor(dest):
|
elif isCursor(dest):
|
||||||
case ri.kind:
|
case ri.kind:
|
||||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
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
|
# We know the result will be a stmt so we use that fact to optimize
|
||||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||||
else:
|
else:
|
||||||
|
|
@ -998,53 +1008,53 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
|
||||||
else:
|
else:
|
||||||
case ri.kind
|
case ri.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
if isUnpackedTuple(ri[0]):
|
if isUnpackedTuple(ri[0]):
|
||||||
# unpacking of tuple: take over the elements
|
# 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):
|
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||||
if aliases(dest, ri) == no:
|
if aliases(dest, ri) == no:
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
if isAtom(ri[1]):
|
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))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
else:
|
else:
|
||||||
result = genFieldAccessSideEffects(c, dest, ri, isDecl)
|
result = genFieldAccessSideEffects(c, dest, ri, flags)
|
||||||
else:
|
else:
|
||||||
result = c.genSink(dest, destructiveMoveVar(ri, c, s), isDecl)
|
result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags)
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri)
|
result = c.genCopy(dest, ri, flags)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
of nkBracket:
|
of nkBracket:
|
||||||
# array constructor
|
# array constructor
|
||||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
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)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
else:
|
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:
|
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:
|
of nkSym:
|
||||||
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
|
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# 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))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
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):
|
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# 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))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri)
|
result = c.genCopy(dest, ri, flags)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
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:
|
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
|
# We know the result will be a stmt so we use that fact to optimize
|
||||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||||
of nkRaiseStmt:
|
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
|
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||||
canBeMoved(c, dest.typ):
|
canBeMoved(c, dest.typ):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# 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))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri)
|
result = c.genCopy(dest, ri, flags)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2711,7 +2711,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of nkReturnStmt: genReturnStmt(p, n)
|
of nkReturnStmt: genReturnStmt(p, n)
|
||||||
of nkBreakStmt: genBreakStmt(p, n)
|
of nkBreakStmt: genBreakStmt(p, n)
|
||||||
of nkAsgn: genAsgn(p, n)
|
of nkAsgn: genAsgn(p, n)
|
||||||
of nkFastAsgn: genFastAsgn(p, n)
|
of nkFastAsgn, nkSinkAsgn: genFastAsgn(p, n)
|
||||||
of nkDiscardStmt:
|
of nkDiscardStmt:
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
genLineDir(p, n)
|
genLineDir(p, n)
|
||||||
|
|
|
||||||
|
|
@ -782,7 +782,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||||
n[1] = liftCapturedVars(n[1], owner, d, c)
|
n[1] = liftCapturedVars(n[1], owner, d, c)
|
||||||
if n[1].kind == nkClosure: result = n[1]
|
if n[1].kind == nkClosure: result = n[1]
|
||||||
of nkReturnStmt:
|
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
|
# we have a `result = result` expression produced by the closure
|
||||||
# transform, let's not touch the LHS in order to make the lifting pass
|
# transform, let's not touch the LHS in order to make the lifting pass
|
||||||
# correct when `result` is lifted
|
# correct when `result` is lifted
|
||||||
|
|
|
||||||
|
|
@ -1242,7 +1242,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
|
||||||
result = check(n.sons[0], ctx, map)
|
result = check(n.sons[0], ctx, map)
|
||||||
of nkIfStmt, nkIfExpr:
|
of nkIfStmt, nkIfExpr:
|
||||||
result = checkIf(n, ctx, map)
|
result = checkIf(n, ctx, map)
|
||||||
of nkAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
result = checkAsgn(n[0], n[1], ctx, map)
|
result = checkAsgn(n[0], n[1], ctx, map)
|
||||||
of nkVarSection:
|
of nkVarSection:
|
||||||
result.map = map
|
result.map = map
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||||
discard "do not follow the construct"
|
discard "do not follow the construct"
|
||||||
|
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
# reverse order, see remark for `=sink`:
|
# reverse order, see remark for `=sink`:
|
||||||
analyse(c, b, n[1])
|
analyse(c, b, n[1])
|
||||||
analyse(c, b, n[0])
|
analyse(c, b, n[0])
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ proc canonKind(n: PNode): TNodeKind =
|
||||||
case result
|
case result
|
||||||
of nkCallKinds: result = nkCall
|
of nkCallKinds: result = nkCall
|
||||||
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
||||||
of nkFastAsgn: result = nkAsgn
|
of nkFastAsgn, nkSinkAsgn: result = nkAsgn
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc sameKinds(a, b: PNode): bool {.inline.} =
|
proc sameKinds(a, b: PNode): bool {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||||
of nkOfInherit: result = lsub(g, n[0]) + len("of_")
|
of nkOfInherit: result = lsub(g, n[0]) + len("of_")
|
||||||
of nkProcTy: result = lsons(g, n) + len("proc_")
|
of nkProcTy: result = lsons(g, n) + len("proc_")
|
||||||
of nkIteratorTy: result = lsons(g, n) + len("iterator_")
|
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:
|
of nkEnumTy:
|
||||||
if n.len > 0:
|
if n.len > 0:
|
||||||
result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_")
|
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)
|
put(g, tkSpaces, Space)
|
||||||
putWithSpace(g, tkEquals, "=")
|
putWithSpace(g, tkEquals, "=")
|
||||||
gsub(g, n, 1)
|
gsub(g, n, 1)
|
||||||
|
of nkSinkAsgn:
|
||||||
|
put(g, tkSymbol, "`=sink`")
|
||||||
|
put(g, tkParLe, "(")
|
||||||
|
gcomma(g, n)
|
||||||
|
put(g, tkParRi, ")")
|
||||||
of nkChckRangeF:
|
of nkChckRangeF:
|
||||||
put(g, tkSymbol, "chckRangeF")
|
put(g, tkSymbol, "chckRangeF")
|
||||||
put(g, tkParLe, "(")
|
put(g, tkParLe, "(")
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
|
result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||||
for i in 0..<n.len: result.add(n[i])
|
for i in 0..<n.len: result.add(n[i])
|
||||||
result = semGenericStmt(c, result, flags, ctx)
|
result = semGenericStmt(c, result, flags, ctx)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
checkSonsLen(n, 2, c.config)
|
checkSonsLen(n, 2, c.config)
|
||||||
let a = n[0]
|
let a = n[0]
|
||||||
let b = n[1]
|
let b = n[1]
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,8 @@ proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||||
result[1] = n.lastSon
|
result[1] = n.lastSon
|
||||||
result = semAsgn(c, result, noOverloadedSubscript)
|
result = semAsgn(c, result, noOverloadedSubscript)
|
||||||
|
|
||||||
proc semAsgnOpr(c: PContext; n: PNode): PNode =
|
proc semAsgnOpr(c: PContext; n: PNode; k: TNodeKind): PNode =
|
||||||
result = newNodeI(nkAsgn, n.info, 2)
|
result = newNodeI(k, n.info, 2)
|
||||||
result[0] = n[1]
|
result[0] = n[1]
|
||||||
result[1] = n[2]
|
result[1] = n[2]
|
||||||
result = semAsgn(c, result, noOverloadedAsgn)
|
result = semAsgn(c, result, noOverloadedAsgn)
|
||||||
|
|
@ -553,7 +553,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
result = semArrPut(c, n, flags)
|
result = semArrPut(c, n, flags)
|
||||||
of mAsgn:
|
of mAsgn:
|
||||||
if n[0].sym.name.s == "=":
|
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:
|
else:
|
||||||
result = semShallowCopy(c, n, flags)
|
result = semShallowCopy(c, n, flags)
|
||||||
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||||
|
|
||||||
proc analyse(c: var AnalysisCtx; n: PNode) =
|
proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
let y = n[1].skipConv
|
let y = n[1].skipConv
|
||||||
if n[0].isSingleAssignable and y.isLocal:
|
if n[0].isSingleAssignable and y.isLocal:
|
||||||
let slot = c.getSlot(y.sym)
|
let slot = c.getSlot(y.sym)
|
||||||
|
|
@ -442,7 +442,7 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier:
|
||||||
else:
|
else:
|
||||||
it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
|
it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
|
||||||
if result.isNil: result = n
|
if result.isNil: result = n
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
let b = n[1]
|
let b = n[1]
|
||||||
if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
|
if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
|
||||||
spawnResult(t, true) == srByVar):
|
spawnResult(t, true) == srByVar):
|
||||||
|
|
|
||||||
|
|
@ -1053,7 +1053,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
checkFieldAccess(tracked.guards, n, tracked.config)
|
checkFieldAccess(tracked.guards, n, tracked.config)
|
||||||
of nkTryStmt: trackTryStmt(tracked, n)
|
of nkTryStmt: trackTryStmt(tracked, n)
|
||||||
of nkPragma: trackPragmaStmt(tracked, n)
|
of nkPragma: trackPragmaStmt(tracked, n)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
track(tracked, n[1])
|
track(tracked, n[1])
|
||||||
initVar(tracked, n[0], volatileCheck=true)
|
initVar(tracked, n[0], volatileCheck=true)
|
||||||
invalidateFacts(tracked.guards, n[0])
|
invalidateFacts(tracked.guards, n[0])
|
||||||
|
|
|
||||||
|
|
@ -511,7 +511,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||||
for i in 0..<n.len: result.add(n[i])
|
for i in 0..<n.len: result.add(n[i])
|
||||||
result = semTemplBodySons(c, result)
|
result = semTemplBodySons(c, result)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
checkSonsLen(n, 2, c.c.config)
|
checkSonsLen(n, 2, c.c.config)
|
||||||
let a = n[0]
|
let a = n[0]
|
||||||
let b = n[1]
|
let b = n[1]
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ proc findDocComment(n: PNode): PNode =
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
if n.len > 1:
|
if n.len > 1:
|
||||||
result = findDocComment(n[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])
|
result = findDocComment(n[1])
|
||||||
|
|
||||||
proc extractDocComment(g: ModuleGraph; s: PSym): string =
|
proc extractDocComment(g: ModuleGraph; s: PSym): string =
|
||||||
|
|
|
||||||
|
|
@ -686,7 +686,7 @@ proc traverse(c: var Partitions; n: PNode) =
|
||||||
for i in 0..<child.len-2:
|
for i in 0..<child.len-2:
|
||||||
#registerVariable(c, child[i])
|
#registerVariable(c, child[i])
|
||||||
deps(c, child[i], last)
|
deps(c, child[i], last)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
traverse(c, n[0])
|
traverse(c, n[0])
|
||||||
inc c.inAsgnSource
|
inc c.inAsgnSource
|
||||||
traverse(c, n[1])
|
traverse(c, n[1])
|
||||||
|
|
@ -811,7 +811,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
|
||||||
registerVariable(c, child[i])
|
registerVariable(c, child[i])
|
||||||
#deps(c, child[i], last)
|
#deps(c, child[i], last)
|
||||||
|
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
computeLiveRanges(c, n[0])
|
computeLiveRanges(c, n[0])
|
||||||
computeLiveRanges(c, n[1])
|
computeLiveRanges(c, n[1])
|
||||||
if n[0].kind == nkSym:
|
if n[0].kind == nkSym:
|
||||||
|
|
|
||||||
|
|
@ -2045,7 +2045,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
of nkNilLit:
|
of nkNilLit:
|
||||||
if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
|
if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
|
||||||
else: unused(c, n, dest)
|
else: unused(c, n, dest)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
genAsgn(c, n[0], n[1], n.kind == nkAsgn)
|
genAsgn(c, n[0], n[1], n.kind == nkAsgn)
|
||||||
of nkDotExpr: genObjAccess(c, n, dest, flags)
|
of nkDotExpr: genObjAccess(c, n, dest, flags)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ type
|
||||||
nnkDistinctTy,
|
nnkDistinctTy,
|
||||||
nnkProcTy,
|
nnkProcTy,
|
||||||
nnkIteratorTy, # iterator type
|
nnkIteratorTy, # iterator type
|
||||||
nnkSharedTy, # 'shared T'
|
nnkSinkAsgn,
|
||||||
nnkEnumTy,
|
nnkEnumTy,
|
||||||
nnkEnumFieldDef,
|
nnkEnumFieldDef,
|
||||||
nnkArgList, nnkPattern
|
nnkArgList, nnkPattern
|
||||||
|
|
@ -127,6 +127,7 @@ type
|
||||||
|
|
||||||
const
|
const
|
||||||
nnkMutableTy* {.deprecated.} = nnkOutTy
|
nnkMutableTy* {.deprecated.} = nnkOutTy
|
||||||
|
nnkSharedTy* {.deprecated.} = nnkSinkAsgn
|
||||||
|
|
||||||
type
|
type
|
||||||
NimIdent* {.deprecated.} = object of RootObj
|
NimIdent* {.deprecated.} = object of RootObj
|
||||||
|
|
|
||||||
29
tests/arc/texplicit_sink.nim
Normal file
29
tests/arc/texplicit_sink.nim
Normal 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()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue