Skip to content

Commit 0d02bee

Browse files
metagnAraq
authored andcommitted
round out tuple unpacking assignment, support underscores (#22537)
* round out tuple unpacking assignment, support underscores fixes #18710 * fix test messages * use discard instead of continue Co-authored-by: Andreas Rumpf <[email protected]> --------- Co-authored-by: Andreas Rumpf <[email protected]> (cherry picked from commit 53d43e9)
1 parent c39a013 commit 0d02bee

File tree

7 files changed

+60
-33
lines changed

7 files changed

+60
-33
lines changed

compiler/lowerings.nim

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -122,25 +122,6 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
122122
proc newTryFinally*(body, final: PNode): PNode =
123123
result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
124124

125-
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
126-
let value = n.lastSon
127-
result = newNodeI(nkStmtList, n.info)
128-
129-
var temp = newSym(skTemp, getIdent(g.cache, "_"), idgen, owner, value.info, owner.options)
130-
var v = newNodeI(nkLetSection, value.info)
131-
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
132-
133-
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
134-
vpart[0] = tempAsNode
135-
vpart[1] = newNodeI(nkTupleClassTy, value.info)
136-
vpart[2] = value
137-
v.add vpart
138-
result.add(v)
139-
140-
let lhs = n[0]
141-
for i in 0..<lhs.len:
142-
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
143-
144125
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
145126
result = newNodeI(nkStmtList, n.info)
146127
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(

compiler/semexprs.nim

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,37 @@ proc goodLineInfo(arg: PNode): TLineInfo =
18171817
else:
18181818
arg.info
18191819

1820+
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
1821+
## expand tuple unpacking assignment into series of assignments
1822+
##
1823+
## mirrored with semstmts.makeVarTupleSection
1824+
let lhs = n[0]
1825+
let value = semExprWithType(c, n[1], {efTypeAllowed})
1826+
if value.typ.kind != tyTuple:
1827+
localError(c.config, n[1].info, errXExpected, "tuple")
1828+
elif lhs.len != value.typ.len:
1829+
localError(c.config, n.info, errWrongNumberOfVariables)
1830+
result = newNodeI(nkStmtList, n.info)
1831+
1832+
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
1833+
temp.typ = value.typ
1834+
temp.flags.incl(sfGenSym)
1835+
var v = newNodeI(nkLetSection, value.info)
1836+
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
1837+
var vpart = newNodeI(nkIdentDefs, v.info, 3)
1838+
vpart[0] = tempNode
1839+
vpart[1] = c.graph.emptyNode
1840+
vpart[2] = value
1841+
v.add vpart
1842+
result.add(v)
1843+
1844+
for i in 0..<lhs.len:
1845+
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
1846+
# skip _ assignments if we are using a temp as they are already evaluated
1847+
discard
1848+
else:
1849+
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
1850+
18201851
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
18211852
checkSonsLen(n, 2, c.config)
18221853
var a = n[0]
@@ -1859,7 +1890,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
18591890
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
18601891
# that overloading of the assignment operator still works. Usually we
18611892
# prefer to do these rewritings in transf.nim:
1862-
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
1893+
return semStmt(c, makeTupleAssignments(c, n), {})
18631894
else:
18641895
a = semExprWithType(c, a, {efLValue})
18651896
else:

compiler/semstmts.nim

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -592,30 +592,33 @@ proc globalVarInitCheck(c: PContext, n: PNode) =
592592

593593
proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
594594
## expand tuple unpacking assignments into new var/let/const section
595+
##
596+
## mirrored with semexprs.makeTupleAssignments
595597
if typ.kind != tyTuple:
596598
localError(c.config, a.info, errXExpected, "tuple")
597599
elif a.len-2 != typ.len:
598600
localError(c.config, a.info, errWrongNumberOfVariables)
599601
var
600-
tmpTuple: PSym
602+
tempNode: PNode = nil
601603
lastDef: PNode
602604
let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
603605
# temporary not needed if not const and RHS is tuple literal
604606
# const breaks with seqs without temporary
605607
let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
606608
if useTemp:
607609
# use same symkind for compatibility with original section
608-
tmpTuple = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
609-
tmpTuple.typ = typ
610-
tmpTuple.flags.incl(sfGenSym)
610+
let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
611+
temp.typ = typ
612+
temp.flags.incl(sfGenSym)
611613
lastDef = newNodeI(defkind, a.info)
612614
newSons(lastDef, 3)
613-
lastDef[0] = newSymNode(tmpTuple)
615+
lastDef[0] = newSymNode(temp)
614616
# NOTE: at the moment this is always ast.emptyNode, see parser.nim
615617
lastDef[1] = a[^2]
616618
lastDef[2] = def
617-
tmpTuple.ast = lastDef
619+
temp.ast = lastDef
618620
addToVarSection(c, origResult, n, lastDef)
621+
tempNode = newSymNode(temp)
619622
result = newNodeI(n.kind, a.info)
620623
for j in 0..<a.len-2:
621624
let name = a[j]
@@ -634,7 +637,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
634637
lastDef[0] = name
635638
lastDef[^2] = c.graph.emptyNode
636639
if useTemp:
637-
lastDef[^1] = newTreeIT(nkBracketExpr, name.info, typ[j], newSymNode(tmpTuple), newIntNode(nkIntLit, j))
640+
lastDef[^1] = newTupleAccessRaw(tempNode, j)
638641
else:
639642
var val = def[j]
640643
if val.kind == nkExprColonExpr: val = val[1]

tests/arc/topt_no_cursor.nim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ var
3939
lresult
4040
lvalue
4141
lnext
42-
_
42+
tmpTupleAsgn
4343
lresult = @[123]
44-
_ = (
44+
tmpTupleAsgn = (
4545
let blitTmp = lresult
4646
blitTmp, ";")
47-
lvalue = _[0]
48-
lnext = _[1]
47+
lvalue = tmpTupleAsgn[0]
48+
lnext = tmpTupleAsgn[1]
4949
`=sink`(result.value, move lvalue)
5050
`=destroy`(lnext)
5151
`=destroy_1`(lvalue)

tests/errmsgs/tassignunpack.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
var a, b = 0
22
(a, b) = 1 #[tt.Error
3-
^ type mismatch: got <int literal(1)> but expected 'tuple']#
3+
^ 'tuple' expected]#

tests/tuples/ttuples_various.nim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,15 @@ block: # bug #22054
197197

198198
var v = A(field: (a: 1314))
199199
doAssert get(v)[0] == 1314
200+
201+
block: # tuple unpacking assignment with underscore
202+
var
203+
a = 1
204+
b = 2
205+
doAssert (a, b) == (1, 2)
206+
(a, _) = (3, 4)
207+
doAssert (a, b) == (3, 2)
208+
(_, a) = (5, 6)
209+
doAssert (a, b) == (6, 2)
210+
(b, _) = (7, 8)
211+
doAssert (a, b) == (6, 7)

tests/types/tassignemptytuple.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
discard """
2-
errormsg: "invalid type: 'empty' in this context: '(seq[empty], (seq[empty], set[empty]))' for let"
2+
errormsg: "cannot infer the type of the tuple"
33
file: "tassignemptytuple.nim"
44
line: 11
55
"""

0 commit comments

Comments
 (0)