Skip to content

Commit 53d43e9

Browse files
metagnAraq
andauthored
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]>
1 parent 03f267c commit 53d43e9

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
@@ -1828,6 +1828,37 @@ proc goodLineInfo(arg: PNode): TLineInfo =
18281828
else:
18291829
arg.info
18301830

1831+
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
1832+
## expand tuple unpacking assignment into series of assignments
1833+
##
1834+
## mirrored with semstmts.makeVarTupleSection
1835+
let lhs = n[0]
1836+
let value = semExprWithType(c, n[1], {efTypeAllowed})
1837+
if value.typ.kind != tyTuple:
1838+
localError(c.config, n[1].info, errXExpected, "tuple")
1839+
elif lhs.len != value.typ.len:
1840+
localError(c.config, n.info, errWrongNumberOfVariables)
1841+
result = newNodeI(nkStmtList, n.info)
1842+
1843+
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
1844+
temp.typ = value.typ
1845+
temp.flags.incl(sfGenSym)
1846+
var v = newNodeI(nkLetSection, value.info)
1847+
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
1848+
var vpart = newNodeI(nkIdentDefs, v.info, 3)
1849+
vpart[0] = tempNode
1850+
vpart[1] = c.graph.emptyNode
1851+
vpart[2] = value
1852+
v.add vpart
1853+
result.add(v)
1854+
1855+
for i in 0..<lhs.len:
1856+
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
1857+
# skip _ assignments if we are using a temp as they are already evaluated
1858+
discard
1859+
else:
1860+
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
1861+
18311862
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
18321863
checkSonsLen(n, 2, c.config)
18331864
var a = n[0]
@@ -1870,7 +1901,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
18701901
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
18711902
# that overloading of the assignment operator still works. Usually we
18721903
# prefer to do these rewritings in transf.nim:
1873-
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
1904+
return semStmt(c, makeTupleAssignments(c, n), {})
18741905
else:
18751906
a = semExprWithType(c, a, {efLValue})
18761907
else:

compiler/semstmts.nim

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

600600
proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
601601
## expand tuple unpacking assignments into new var/let/const section
602+
##
603+
## mirrored with semexprs.makeTupleAssignments
602604
if typ.kind != tyTuple:
603605
localError(c.config, a.info, errXExpected, "tuple")
604606
elif a.len-2 != typ.len:
605607
localError(c.config, a.info, errWrongNumberOfVariables)
606608
var
607-
tmpTuple: PSym = nil
609+
tempNode: PNode = nil
608610
lastDef: PNode
609611
let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
610612
# temporary not needed if not const and RHS is tuple literal
611613
# const breaks with seqs without temporary
612614
let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
613615
if useTemp:
614616
# use same symkind for compatibility with original section
615-
tmpTuple = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
616-
tmpTuple.typ = typ
617-
tmpTuple.flags.incl(sfGenSym)
617+
let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
618+
temp.typ = typ
619+
temp.flags.incl(sfGenSym)
618620
lastDef = newNodeI(defkind, a.info)
619621
newSons(lastDef, 3)
620-
lastDef[0] = newSymNode(tmpTuple)
622+
lastDef[0] = newSymNode(temp)
621623
# NOTE: at the moment this is always ast.emptyNode, see parser.nim
622624
lastDef[1] = a[^2]
623625
lastDef[2] = def
624-
tmpTuple.ast = lastDef
626+
temp.ast = lastDef
625627
addToVarSection(c, origResult, n, lastDef)
628+
tempNode = newSymNode(temp)
626629
result = newNodeI(n.kind, a.info)
627630
for j in 0..<a.len-2:
628631
let name = a[j]
@@ -641,7 +644,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
641644
lastDef[0] = name
642645
lastDef[^2] = c.graph.emptyNode
643646
if useTemp:
644-
lastDef[^1] = newTreeIT(nkBracketExpr, name.info, typ[j], newSymNode(tmpTuple), newIntNode(nkIntLit, j))
647+
lastDef[^1] = newTupleAccessRaw(tempNode, j)
645648
else:
646649
var val = def[j]
647650
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)