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
|
||||
pragmas for further analysis by macros
|
||||
- Custom pragmas are now supported for `var` and `let` symbols.
|
||||
- Tuple unpacking is now supported for constants and for loop variables.
|
||||
|
||||
|
||||
### Language changes
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
|||
a: TLoc
|
||||
i: int
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
let tupType = a.t.skipTypes(abstractInst)
|
||||
let tupType = a.t.skipTypes(abstractInst+{tyVar})
|
||||
assert tupType.kind == tyTuple
|
||||
d.inheritLocation(a)
|
||||
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)
|
||||
|
||||
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
|
||||
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)
|
||||
if tup.kind == nkHiddenAddr:
|
||||
result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}))
|
||||
result.addSon(newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}).sons[i]))
|
||||
addSon(result[0], tup[0])
|
||||
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
||||
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) =
|
||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
|
|
|
|||
|
|
@ -1153,18 +1153,26 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
|||
result.addSon list
|
||||
parseSymbolList(p, list)
|
||||
|
||||
proc parseVarTuple(p: var TParser): PNode
|
||||
|
||||
proc parseFor(p: var TParser): PNode =
|
||||
#| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
|
||||
#| forExpr = forStmt
|
||||
result = newNodeP(nkForStmt, p)
|
||||
getTokNoInd(p)
|
||||
var a = identWithPragma(p)
|
||||
addSon(result, a)
|
||||
while p.tok.tokType == tkComma:
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
a = identWithPragma(p)
|
||||
result = newNodeP(nkForStmt, p)
|
||||
if p.tok.tokType == tkParLe:
|
||||
addSon(result, parseVarTuple(p))
|
||||
else:
|
||||
var a = identWithPragma(p)
|
||||
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)
|
||||
addSon(result, parseExpr(p))
|
||||
colcom(p, result)
|
||||
|
|
@ -2048,14 +2056,15 @@ proc parseVarTuple(p: var TParser): PNode =
|
|||
addSon(result, p.emptyNode) # no type desc
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
eat(p, tkEquals)
|
||||
optInd(p, result)
|
||||
addSon(result, parseExpr(p))
|
||||
|
||||
proc parseVariable(p: var TParser): PNode =
|
||||
#| colonBody = colcom stmt doBlocks?
|
||||
#| 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})
|
||||
result[^1] = postExprBlocks(p, result[^1])
|
||||
indAndComment(p, result)
|
||||
|
|
@ -2072,10 +2081,10 @@ proc parseConstant(p: var TParser): PNode =
|
|||
addSon(result, parseTypeDesc(p))
|
||||
else:
|
||||
addSon(result, p.emptyNode)
|
||||
eat(p, tkEquals)
|
||||
optInd(p, result)
|
||||
addSon(result, parseExpr(p))
|
||||
indAndComment(p, result)
|
||||
eat(p, tkEquals)
|
||||
optInd(p, result)
|
||||
addSon(result, parseExpr(p))
|
||||
indAndComment(p, result)
|
||||
|
||||
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
||||
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
||||
|
|
|
|||
|
|
@ -672,28 +672,66 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
|||
# and thus no tuple unpacking:
|
||||
if iter.kind != tyTuple or length == 3:
|
||||
if length == 3:
|
||||
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)
|
||||
if n.sons[0].kind == nkVarTuple:
|
||||
var mutable = false
|
||||
if iter.kind == tyVar:
|
||||
iter = iter.skipTypes({tyVar})
|
||||
mutable = true
|
||||
if sonsLen(n[0])-1 != sonsLen(iter):
|
||||
localError(c.config, n[0].info, errWrongNumberOfVariables)
|
||||
for i in 0 ..< sonsLen(n[0])-1:
|
||||
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:
|
||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||
elif length-2 != sonsLen(iter):
|
||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||
else:
|
||||
for i in countup(0, length - 3):
|
||||
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)
|
||||
if n.sons[i].kind == nkVarTuple:
|
||||
var mutable = false
|
||||
if iter[i].kind == tyVar:
|
||||
iter[i] = iter[i].skipTypes({tyVar})
|
||||
mutable = true
|
||||
if sonsLen(n[i])-1 != sonsLen(iter[i]):
|
||||
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)
|
||||
openScope(c)
|
||||
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):
|
||||
var v = e.sons[i]
|
||||
if v.kind == nkExprColonExpr: v = v.sons[1]
|
||||
let lhs = c.transCon.forStmt.sons[i]
|
||||
let rhs = transform(c, v)
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
if c.transCon.forStmt[i].kind == nkVarTuple:
|
||||
for j in 0 ..< sonsLen(c.transCon.forStmt[i])-1:
|
||||
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:
|
||||
# Unpack the tuple into the loop variables
|
||||
# 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))
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
else:
|
||||
let lhs = c.transCon.forStmt.sons[0]
|
||||
let rhs = transform(c, e)
|
||||
add(result, asgnTo(lhs, rhs))
|
||||
if c.transCon.forStmt.sons[0].kind == nkVarTuple:
|
||||
for i in 0 ..< sonsLen(c.transCon.forStmt[0])-1:
|
||||
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)
|
||||
if c.transCon.yieldStmts <= 1:
|
||||
|
|
@ -609,7 +621,11 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
|||
|
||||
var v = newNodeI(nkVarSection, n.info)
|
||||
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)
|
||||
|
||||
# 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