Tuple unpacking now works for for vars (#10152)
* Tuple unpacking now works for `for` vars * Give error if length of tuple vars != length of tuple * Fix error message showing wrong tuple length * unpacking now works now for mutable items * Update changelog
This commit is contained in:
parent
e89aaaeaab
commit
9884129058
7 changed files with 156 additions and 45 deletions
|
|
@ -165,6 +165,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
- Pragma blocks are no longer eliminated from the typed AST tree to preserve
|
- Pragma blocks are no longer eliminated from the typed AST tree to preserve
|
||||||
pragmas for further analysis by macros
|
pragmas for further analysis by macros
|
||||||
- Custom pragmas are now supported for `var` and `let` symbols.
|
- Custom pragmas are now supported for `var` and `let` symbols.
|
||||||
|
- Tuple unpacking is now supported for constants and for loop variables.
|
||||||
|
|
||||||
|
|
||||||
### Language changes
|
### Language changes
|
||||||
|
|
|
||||||
|
|
@ -752,7 +752,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
||||||
a: TLoc
|
a: TLoc
|
||||||
i: int
|
i: int
|
||||||
initLocExpr(p, e.sons[0], a)
|
initLocExpr(p, e.sons[0], a)
|
||||||
let tupType = a.t.skipTypes(abstractInst)
|
let tupType = a.t.skipTypes(abstractInst+{tyVar})
|
||||||
assert tupType.kind == tyTuple
|
assert tupType.kind == tyTuple
|
||||||
d.inheritLocation(a)
|
d.inheritLocation(a)
|
||||||
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,20 @@ proc newDeref*(n: PNode): PNode {.inline.} =
|
||||||
addSon(result, n)
|
addSon(result, n)
|
||||||
|
|
||||||
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
|
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
|
||||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
if tup.kind == nkHiddenAddr:
|
||||||
abstractInst).sons[i])
|
result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}))
|
||||||
addSon(result, copyTree(tup))
|
result.addSon(newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}).sons[i]))
|
||||||
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
addSon(result[0], tup[0])
|
||||||
lit.intVal = i
|
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
||||||
addSon(result, lit)
|
lit.intVal = i
|
||||||
|
addSon(result[0], lit)
|
||||||
|
else:
|
||||||
|
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||||
|
abstractInst).sons[i])
|
||||||
|
addSon(result, copyTree(tup))
|
||||||
|
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
||||||
|
lit.intVal = i
|
||||||
|
addSon(result, lit)
|
||||||
|
|
||||||
proc addVar*(father, v: PNode) =
|
proc addVar*(father, v: PNode) =
|
||||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||||
|
|
|
||||||
|
|
@ -1153,18 +1153,26 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||||
result.addSon list
|
result.addSon list
|
||||||
parseSymbolList(p, list)
|
parseSymbolList(p, list)
|
||||||
|
|
||||||
|
proc parseVarTuple(p: var TParser): PNode
|
||||||
|
|
||||||
proc parseFor(p: var TParser): PNode =
|
proc parseFor(p: var TParser): PNode =
|
||||||
#| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
|
#| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
|
||||||
#| forExpr = forStmt
|
#| forExpr = forStmt
|
||||||
result = newNodeP(nkForStmt, p)
|
|
||||||
getTokNoInd(p)
|
getTokNoInd(p)
|
||||||
var a = identWithPragma(p)
|
result = newNodeP(nkForStmt, p)
|
||||||
addSon(result, a)
|
if p.tok.tokType == tkParLe:
|
||||||
while p.tok.tokType == tkComma:
|
addSon(result, parseVarTuple(p))
|
||||||
getTok(p)
|
else:
|
||||||
optInd(p, a)
|
var a = identWithPragma(p)
|
||||||
a = identWithPragma(p)
|
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
|
while p.tok.tokType == tkComma:
|
||||||
|
getTok(p)
|
||||||
|
optInd(p, a)
|
||||||
|
if p.tok.tokType == tkParLe:
|
||||||
|
addSon(result, parseVarTuple(p))
|
||||||
|
break
|
||||||
|
a = identWithPragma(p)
|
||||||
|
addSon(result, a)
|
||||||
eat(p, tkIn)
|
eat(p, tkIn)
|
||||||
addSon(result, parseExpr(p))
|
addSon(result, parseExpr(p))
|
||||||
colcom(p, result)
|
colcom(p, result)
|
||||||
|
|
@ -2048,14 +2056,15 @@ proc parseVarTuple(p: var TParser): PNode =
|
||||||
addSon(result, p.emptyNode) # no type desc
|
addSon(result, p.emptyNode) # no type desc
|
||||||
optPar(p)
|
optPar(p)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
eat(p, tkEquals)
|
|
||||||
optInd(p, result)
|
|
||||||
addSon(result, parseExpr(p))
|
|
||||||
|
|
||||||
proc parseVariable(p: var TParser): PNode =
|
proc parseVariable(p: var TParser): PNode =
|
||||||
#| colonBody = colcom stmt doBlocks?
|
#| colonBody = colcom stmt doBlocks?
|
||||||
#| variable = (varTuple / identColonEquals) colonBody? indAndComment
|
#| variable = (varTuple / identColonEquals) colonBody? indAndComment
|
||||||
if p.tok.tokType == tkParLe: result = parseVarTuple(p)
|
if p.tok.tokType == tkParLe:
|
||||||
|
result = parseVarTuple(p)
|
||||||
|
eat(p, tkEquals)
|
||||||
|
optInd(p, result)
|
||||||
|
addSon(result, parseExpr(p))
|
||||||
else: result = parseIdentColonEquals(p, {withPragma, withDot})
|
else: result = parseIdentColonEquals(p, {withPragma, withDot})
|
||||||
result[^1] = postExprBlocks(p, result[^1])
|
result[^1] = postExprBlocks(p, result[^1])
|
||||||
indAndComment(p, result)
|
indAndComment(p, result)
|
||||||
|
|
@ -2072,10 +2081,10 @@ proc parseConstant(p: var TParser): PNode =
|
||||||
addSon(result, parseTypeDesc(p))
|
addSon(result, parseTypeDesc(p))
|
||||||
else:
|
else:
|
||||||
addSon(result, p.emptyNode)
|
addSon(result, p.emptyNode)
|
||||||
eat(p, tkEquals)
|
eat(p, tkEquals)
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
addSon(result, parseExpr(p))
|
addSon(result, parseExpr(p))
|
||||||
indAndComment(p, result)
|
indAndComment(p, result)
|
||||||
|
|
||||||
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
||||||
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
||||||
|
|
|
||||||
|
|
@ -672,28 +672,66 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
# and thus no tuple unpacking:
|
# and thus no tuple unpacking:
|
||||||
if iter.kind != tyTuple or length == 3:
|
if iter.kind != tyTuple or length == 3:
|
||||||
if length == 3:
|
if length == 3:
|
||||||
var v = symForVar(c, n.sons[0])
|
if n.sons[0].kind == nkVarTuple:
|
||||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
var mutable = false
|
||||||
# BUGFIX: don't use `iter` here as that would strip away
|
if iter.kind == tyVar:
|
||||||
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
iter = iter.skipTypes({tyVar})
|
||||||
# for an example:
|
mutable = true
|
||||||
v.typ = iterBase
|
if sonsLen(n[0])-1 != sonsLen(iter):
|
||||||
n.sons[0] = newSymNode(v)
|
localError(c.config, n[0].info, errWrongNumberOfVariables)
|
||||||
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
for i in 0 ..< sonsLen(n[0])-1:
|
||||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
var v = symForVar(c, n[0][i])
|
||||||
|
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||||
|
if mutable:
|
||||||
|
v.typ = newTypeS(tyVar, c)
|
||||||
|
v.typ.sons.add iter[i]
|
||||||
|
else:
|
||||||
|
v.typ = iter.sons[i]
|
||||||
|
n.sons[0][i] = newSymNode(v)
|
||||||
|
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
||||||
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
|
else:
|
||||||
|
var v = symForVar(c, n.sons[0])
|
||||||
|
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||||
|
# BUGFIX: don't use `iter` here as that would strip away
|
||||||
|
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
||||||
|
# for an example:
|
||||||
|
v.typ = iterBase
|
||||||
|
n.sons[0] = newSymNode(v)
|
||||||
|
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
||||||
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||||
elif length-2 != sonsLen(iter):
|
elif length-2 != sonsLen(iter):
|
||||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||||
else:
|
else:
|
||||||
for i in countup(0, length - 3):
|
for i in countup(0, length - 3):
|
||||||
var v = symForVar(c, n.sons[i])
|
if n.sons[i].kind == nkVarTuple:
|
||||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
var mutable = false
|
||||||
v.typ = iter.sons[i]
|
if iter[i].kind == tyVar:
|
||||||
n.sons[i] = newSymNode(v)
|
iter[i] = iter[i].skipTypes({tyVar})
|
||||||
if sfGenSym notin v.flags:
|
mutable = true
|
||||||
if not isDiscardUnderscore(v): addForVarDecl(c, v)
|
if sonsLen(n[i])-1 != sonsLen(iter[i]):
|
||||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
localError(c.config, n[i].info, errWrongNumberOfVariables)
|
||||||
|
for j in 0 ..< sonsLen(n[i])-1:
|
||||||
|
var v = symForVar(c, n[i][j])
|
||||||
|
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||||
|
if mutable:
|
||||||
|
v.typ = newTypeS(tyVar, c)
|
||||||
|
v.typ.sons.add iter[i][j]
|
||||||
|
else:
|
||||||
|
v.typ = iter[i][j]
|
||||||
|
n.sons[i][j] = newSymNode(v)
|
||||||
|
if not isDiscardUnderscore(v): addForVarDecl(c, v)
|
||||||
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
|
else:
|
||||||
|
var v = symForVar(c, n.sons[i])
|
||||||
|
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||||
|
v.typ = iter.sons[i]
|
||||||
|
n.sons[i] = newSymNode(v)
|
||||||
|
if sfGenSym notin v.flags:
|
||||||
|
if not isDiscardUnderscore(v): addForVarDecl(c, v)
|
||||||
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
||||||
|
|
|
||||||
|
|
@ -378,9 +378,15 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
|
||||||
for i in countup(0, sonsLen(e) - 1):
|
for i in countup(0, sonsLen(e) - 1):
|
||||||
var v = e.sons[i]
|
var v = e.sons[i]
|
||||||
if v.kind == nkExprColonExpr: v = v.sons[1]
|
if v.kind == nkExprColonExpr: v = v.sons[1]
|
||||||
let lhs = c.transCon.forStmt.sons[i]
|
if c.transCon.forStmt[i].kind == nkVarTuple:
|
||||||
let rhs = transform(c, v)
|
for j in 0 ..< sonsLen(c.transCon.forStmt[i])-1:
|
||||||
add(result, asgnTo(lhs, rhs))
|
let lhs = c.transCon.forStmt[i][j]
|
||||||
|
let rhs = transform(c, newTupleAccess(c.graph, v, j))
|
||||||
|
add(result, asgnTo(lhs, rhs))
|
||||||
|
else:
|
||||||
|
let lhs = c.transCon.forStmt.sons[i]
|
||||||
|
let rhs = transform(c, v)
|
||||||
|
add(result, asgnTo(lhs, rhs))
|
||||||
else:
|
else:
|
||||||
# Unpack the tuple into the loop variables
|
# Unpack the tuple into the loop variables
|
||||||
# XXX: BUG: what if `n` is an expression with side-effects?
|
# XXX: BUG: what if `n` is an expression with side-effects?
|
||||||
|
|
@ -389,9 +395,15 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
|
||||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||||
add(result, asgnTo(lhs, rhs))
|
add(result, asgnTo(lhs, rhs))
|
||||||
else:
|
else:
|
||||||
let lhs = c.transCon.forStmt.sons[0]
|
if c.transCon.forStmt.sons[0].kind == nkVarTuple:
|
||||||
let rhs = transform(c, e)
|
for i in 0 ..< sonsLen(c.transCon.forStmt[0])-1:
|
||||||
add(result, asgnTo(lhs, rhs))
|
let lhs = c.transCon.forStmt[0][i]
|
||||||
|
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||||
|
add(result, asgnTo(lhs, rhs))
|
||||||
|
else:
|
||||||
|
let lhs = c.transCon.forStmt.sons[0]
|
||||||
|
let rhs = transform(c, e)
|
||||||
|
add(result, asgnTo(lhs, rhs))
|
||||||
|
|
||||||
inc(c.transCon.yieldStmts)
|
inc(c.transCon.yieldStmts)
|
||||||
if c.transCon.yieldStmts <= 1:
|
if c.transCon.yieldStmts <= 1:
|
||||||
|
|
@ -609,7 +621,11 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
|
|
||||||
var v = newNodeI(nkVarSection, n.info)
|
var v = newNodeI(nkVarSection, n.info)
|
||||||
for i in countup(0, length - 3):
|
for i in countup(0, length - 3):
|
||||||
addVar(v, copyTree(n.sons[i])) # declare new vars
|
if n[i].kind == nkVarTuple:
|
||||||
|
for j in 0 ..< sonsLen(n[i])-1:
|
||||||
|
addVar(v, copyTree(n[i][j])) # declare new vars
|
||||||
|
else:
|
||||||
|
addVar(v, copyTree(n.sons[i])) # declare new vars
|
||||||
add(stmtList, v.PTransNode)
|
add(stmtList, v.PTransNode)
|
||||||
|
|
||||||
# Bugfix: inlined locals belong to the invoking routine, not to the invoked
|
# Bugfix: inlined locals belong to the invoking routine, not to the invoked
|
||||||
|
|
|
||||||
39
tests/tuples/tfortupleunpack.nim
Normal file
39
tests/tuples/tfortupleunpack.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
123
|
||||||
|
113283
|
||||||
|
0
|
||||||
|
123
|
||||||
|
1
|
||||||
|
113283
|
||||||
|
@[(88, 99, 11), (88, 99, 11)]
|
||||||
|
@[(7, 6, -28), (7, 6, -28)]
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
let t1 = (1, 2, 3)
|
||||||
|
let t2 = (11, 32, 83)
|
||||||
|
let s = @[t1, t2]
|
||||||
|
|
||||||
|
for (a, b, c) in s:
|
||||||
|
echo a, b, c
|
||||||
|
|
||||||
|
for i, (a, b, c) in s:
|
||||||
|
echo i
|
||||||
|
echo a, b, c
|
||||||
|
|
||||||
|
var x = @[(1,2,3), (4,5,6)]
|
||||||
|
|
||||||
|
for (a, b, c) in x.mitems:
|
||||||
|
a = 88
|
||||||
|
b = 99
|
||||||
|
c = 11
|
||||||
|
echo x
|
||||||
|
|
||||||
|
for i, (a, b, c) in x.mpairs:
|
||||||
|
a = 7
|
||||||
|
b = 6
|
||||||
|
c = -28
|
||||||
|
echo x
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue