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:
Neelesh Chandola 2019-02-23 18:31:34 +05:30 • committed by Andreas Rumpf
commit 9884129058
7 changed files with 156 additions and 45 deletions

View file

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

View file

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

View file

@ -21,6 +21,14 @@ 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 =
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( result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i]) abstractInst).sons[i])
addSon(result, copyTree(tup)) addSon(result, copyTree(tup))

View file

@ -1153,16 +1153,24 @@ 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)
result = newNodeP(nkForStmt, p)
if p.tok.tokType == tkParLe:
addSon(result, parseVarTuple(p))
else:
var a = identWithPragma(p) var a = identWithPragma(p)
addSon(result, a) addSon(result, a)
while p.tok.tokType == tkComma: while p.tok.tokType == tkComma:
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
if p.tok.tokType == tkParLe:
addSon(result, parseVarTuple(p))
break
a = identWithPragma(p) a = identWithPragma(p)
addSon(result, a) addSon(result, a)
eat(p, tkIn) eat(p, tkIn)
@ -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)

View file

@ -672,6 +672,25 @@ 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:
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]) var v = symForVar(c, n.sons[0])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
# BUGFIX: don't use `iter` here as that would strip away # BUGFIX: don't use `iter` here as that would strip away
@ -687,6 +706,25 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
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):
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]) var v = symForVar(c, n.sons[i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]

View file

@ -378,6 +378,12 @@ 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]
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 lhs = c.transCon.forStmt.sons[i]
let rhs = transform(c, v) let rhs = transform(c, v)
add(result, asgnTo(lhs, rhs)) add(result, asgnTo(lhs, rhs))
@ -388,6 +394,12 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
let lhs = c.transCon.forStmt.sons[i] let lhs = c.transCon.forStmt.sons[i]
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:
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: else:
let lhs = c.transCon.forStmt.sons[0] let lhs = c.transCon.forStmt.sons[0]
let rhs = transform(c, e) let rhs = transform(c, e)
@ -609,6 +621,10 @@ 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):
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 addVar(v, copyTree(n.sons[i])) # declare new vars
add(stmtList, v.PTransNode) add(stmtList, v.PTransNode)

View 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