Fix wrong result in tuple assignment (#9340)

Fixes #9177
This commit is contained in:
LemonBoy
2019-02-08 12:24:03 +01:00
committed by Andreas Rumpf
parent 8bc7c50c86
commit aa6e40abe6
2 changed files with 49 additions and 0 deletions

View File

@@ -321,6 +321,38 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
for i in countup(0, sonsLen(n)-1):
result[i] = introduceNewLocalVars(c, n.sons[i])
proc transformAsgn(c: PTransf, n: PNode): PTransNode =
let rhs = n[1]
if rhs.kind != nkTupleConstr:
return transformSons(c, n)
# Unpack the tuple assignment into N temporary variables and then pack them
# into a tuple: this allows us to get the correct results even when the rhs
# depends on the value of the lhs
let letSection = newTransNode(nkLetSection, n.info, rhs.len)
let newTupleConstr = newTransNode(nkTupleConstr, n.info, rhs.len)
for i, field in rhs:
let val = if field.kind == nkExprColonExpr: field[1] else: field
let def = newTransNode(nkIdentDefs, field.info, 3)
def[0] = PTransNode(newTemp(c, val.typ, field.info))
def[1] = PTransNode(newNodeI(nkEmpty, field.info))
def[2] = transform(c, val)
letSection[i] = def
# NOTE: We assume the constructor fields are in the correct order for the
# given tuple type
newTupleConstr[i] = def[0]
PNode(newTupleConstr).typ = rhs.typ
let asgnNode = newTransNode(nkAsgn, n.info, 2)
asgnNode[0] = transform(c, n[0])
asgnNode[1] = newTupleConstr
result = newTransNode(nkStmtList, n.info, 2)
result[0] = letSection
result[1] = asgnNode
proc transformYield(c: PTransf, n: PNode): PTransNode =
proc asgnTo(lhs: PNode, rhs: PTransNode): PTransNode =
# Choose the right assignment instruction according to the given ``lhs``
@@ -949,6 +981,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformYield(c, n)
else:
result = transformSons(c, n)
of nkAsgn:
result = transformAsgn(c, n)
of nkIdentDefs, nkConstDef:
result = PTransNode(n)
result[0] = transform(c, n[0])

15
tests/tuples/t9177.nim Normal file
View File

@@ -0,0 +1,15 @@
discard """
action: run
"""
block:
var x = (a: 5, b: 1)
x = (3 * x.a + 2 * x.b, x.a + x.b)
doAssert x.a == 17
doAssert x.b == 6
block:
# Transformation of a tuple constructor with named arguments
var x = (a: 5, b: 1)
x = (a: 3 * x.a + 2 * x.b, b: x.a + x.b)
doAssert x.a == 17
doAssert x.b == 6