diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 2 | ||||
-rw-r--r-- | compiler/lowerings.nim | 20 | ||||
-rw-r--r-- | compiler/parser.nim | 39 | ||||
-rw-r--r-- | compiler/semstmts.nim | 70 | ||||
-rw-r--r-- | compiler/transf.nim | 30 | ||||
-rw-r--r-- | tests/tuples/tfortupleunpack.nim | 39 |
7 files changed, 156 insertions, 45 deletions
diff --git a/changelog.md b/changelog.md index 211855bfc..7f6ad1ecb 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7e0437c39..f92f2e4de 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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 diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index d199abcc7..2c1c3c48d 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -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) diff --git a/compiler/parser.nim b/compiler/parser.nim index 01a3ce4d0..260e57cdb 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 363049672..aa0230f2f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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) diff --git a/compiler/transf.nim b/compiler/transf.nim index 071cb00ee..a26598094 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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 diff --git a/tests/tuples/tfortupleunpack.nim b/tests/tuples/tfortupleunpack.nim new file mode 100644 index 000000000..56cf30ebc --- /dev/null +++ b/tests/tuples/tfortupleunpack.nim @@ -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 + + |