summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/lowerings.nim19
-rw-r--r--compiler/semexprs.nim33
-rw-r--r--compiler/semstmts.nim17
-rw-r--r--tests/arc/topt_no_cursor.nim8
-rw-r--r--tests/errmsgs/tassignunpack.nim2
-rw-r--r--tests/tuples/ttuples_various.nim12
-rw-r--r--tests/types/tassignemptytuple.nim2
7 files changed, 60 insertions, 33 deletions
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index a083b9195..42d0f1790 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -122,25 +122,6 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
 proc newTryFinally*(body, final: PNode): PNode =
   result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
-  let value = n.lastSon
-  result = newNodeI(nkStmtList, n.info)
-
-  var temp = newSym(skTemp, getIdent(g.cache, "_"), idgen, owner, value.info, owner.options)
-  var v = newNodeI(nkLetSection, value.info)
-  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
-
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart[0] = tempAsNode
-  vpart[1] = newNodeI(nkTupleClassTy, value.info)
-  vpart[2] = value
-  v.add vpart
-  result.add(v)
-
-  let lhs = n[0]
-  for i in 0..<lhs.len:
-    result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
-
 proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 52d1f0628..cb27ca0ff 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1828,6 +1828,37 @@ proc goodLineInfo(arg: PNode): TLineInfo =
   else:
     arg.info
 
+proc makeTupleAssignments(c: PContext; n: PNode): PNode =
+  ## expand tuple unpacking assignment into series of assignments
+  ## 
+  ## mirrored with semstmts.makeVarTupleSection
+  let lhs = n[0]
+  let value = semExprWithType(c, n[1], {efTypeAllowed})
+  if value.typ.kind != tyTuple:
+    localError(c.config, n[1].info, errXExpected, "tuple")
+  elif lhs.len != value.typ.len:
+    localError(c.config, n.info, errWrongNumberOfVariables)
+  result = newNodeI(nkStmtList, n.info)
+
+  let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
+  temp.typ = value.typ
+  temp.flags.incl(sfGenSym)
+  var v = newNodeI(nkLetSection, value.info)
+  let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = tempNode
+  vpart[1] = c.graph.emptyNode
+  vpart[2] = value
+  v.add vpart
+  result.add(v)
+
+  for i in 0..<lhs.len:
+    if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
+      # skip _ assignments if we are using a temp as they are already evaluated
+      discard
+    else:
+      result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
+
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
   checkSonsLen(n, 2, c.config)
   var a = n[0]
@@ -1870,7 +1901,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
+      return semStmt(c, makeTupleAssignments(c, n), {})
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 39c37f4fb..bb9c474a8 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -599,12 +599,14 @@ proc globalVarInitCheck(c: PContext, n: PNode) =
 
 proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
   ## expand tuple unpacking assignments into new var/let/const section
+  ## 
+  ## mirrored with semexprs.makeTupleAssignments
   if typ.kind != tyTuple:
     localError(c.config, a.info, errXExpected, "tuple")
   elif a.len-2 != typ.len:
     localError(c.config, a.info, errWrongNumberOfVariables)
   var
-    tmpTuple: PSym = nil
+    tempNode: PNode = nil
     lastDef: PNode
   let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
   # temporary not needed if not const and RHS is tuple literal
@@ -612,17 +614,18 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
   let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
   if useTemp:
     # use same symkind for compatibility with original section
-    tmpTuple = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
-    tmpTuple.typ = typ
-    tmpTuple.flags.incl(sfGenSym)
+    let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
+    temp.typ = typ
+    temp.flags.incl(sfGenSym)
     lastDef = newNodeI(defkind, a.info)
     newSons(lastDef, 3)
-    lastDef[0] = newSymNode(tmpTuple)
+    lastDef[0] = newSymNode(temp)
     # NOTE: at the moment this is always ast.emptyNode, see parser.nim
     lastDef[1] = a[^2]
     lastDef[2] = def
-    tmpTuple.ast = lastDef
+    temp.ast = lastDef
     addToVarSection(c, origResult, n, lastDef)
+    tempNode = newSymNode(temp)
   result = newNodeI(n.kind, a.info)
   for j in 0..<a.len-2:
     let name = a[j]
@@ -641,7 +644,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
       lastDef[0] = name
     lastDef[^2] = c.graph.emptyNode
     if useTemp:
-      lastDef[^1] = newTreeIT(nkBracketExpr, name.info, typ[j], newSymNode(tmpTuple), newIntNode(nkIntLit, j))
+      lastDef[^1] = newTupleAccessRaw(tempNode, j)
     else:
       var val = def[j]
       if val.kind == nkExprColonExpr: val = val[1]
diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim
index 7cfb0a0d5..dfb0f0a38 100644
--- a/tests/arc/topt_no_cursor.nim
+++ b/tests/arc/topt_no_cursor.nim
@@ -39,13 +39,13 @@ var
   lresult
   lvalue
   lnext
-  _
+  tmpTupleAsgn
 lresult = @[123]
-_ = (
+tmpTupleAsgn = (
   let blitTmp = lresult
   blitTmp, ";")
-lvalue = _[0]
-lnext = _[1]
+lvalue = tmpTupleAsgn[0]
+lnext = tmpTupleAsgn[1]
 `=sink`(result.value, move lvalue)
 `=destroy`(lnext)
 `=destroy_1`(lvalue)
diff --git a/tests/errmsgs/tassignunpack.nim b/tests/errmsgs/tassignunpack.nim
index 27413a42b..d74e16dd5 100644
--- a/tests/errmsgs/tassignunpack.nim
+++ b/tests/errmsgs/tassignunpack.nim
@@ -1,3 +1,3 @@
 var a, b = 0
 (a, b) = 1 #[tt.Error
-         ^ type mismatch: got <int literal(1)> but expected 'tuple']#
+         ^ 'tuple' expected]#
diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim
index 97bc70bd2..e392731d2 100644
--- a/tests/tuples/ttuples_various.nim
+++ b/tests/tuples/ttuples_various.nim
@@ -197,3 +197,15 @@ block: # bug #22054
 
   var v = A(field: (a: 1314))
   doAssert get(v)[0] == 1314
+
+block: # tuple unpacking assignment with underscore
+  var
+    a = 1
+    b = 2
+  doAssert (a, b) == (1, 2)
+  (a, _) = (3, 4)
+  doAssert (a, b) == (3, 2)
+  (_, a) = (5, 6)
+  doAssert (a, b) == (6, 2)
+  (b, _) = (7, 8)
+  doAssert (a, b) == (6, 7)
diff --git a/tests/types/tassignemptytuple.nim b/tests/types/tassignemptytuple.nim
index f3320dec7..9d5a311ba 100644
--- a/tests/types/tassignemptytuple.nim
+++ b/tests/types/tassignemptytuple.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid type: 'empty' in this context: '(seq[empty], (seq[empty], set[empty]))' for let"
+  errormsg: "cannot infer the type of the tuple"
   file: "tassignemptytuple.nim"
   line: 11
 """