[Luau] Incorrect order of multiple assignments

Consider the following code:

local a; a, a = 1, 2; print(a)

In Lua, 1 is returned:

lua

In Luau, 2 is returned:
luau

1 Like

Can you explain why you think the Lua behavior is correct here?

4 Likes

To me, it looks like stock Lua is handling assignments from right to left, but the right to left would seem like the correct behavior given that is how developers write the code. The implementation in Luau seems more correct given that, although this seems like something that wouldn’t be covered in the specification of Lua (or any language with multiple variable assignments) where the variable being assigned is the same variable.

Because I spent several hours debugging code that worked just fine in Lua. When working out an algorithm, I’ll often run it a standalone Lua interpreter. I expect code that works there to also work in Luau. Whether the order of assignment is objectively correct is insignificant.

3 Likes

While consistency between Lua and Luau for variable assignment is important, what is the use case for fixing multiple assignments to the same variable in this case? It is standard practice to create different variable names or to use a throw-away character like _ for variables you don’t need from returning functions.

1 Like

So the creator of Lua has the following to say about assignment:

Evaluation order and assignment order are both explicitly undefined.

My take-away from this is to basically never use multiple assignment. While I would prefer if their behaviors matched, Luau’s current behavior would be acceptable if it is somehow more performant.

That was a minimal reproduction. The actual code looked more like this:

a[i], a[#a] = a[#a], nil
-- vs
a[#a], a[i] = nil, a[#a]
3 Likes

Right, so I’m a bit fuzzy on the details but my recollection is that Lua handles multiple assignments by computing the entire right hand side into temporary registers, and then assigning that to left hand side values.

To avoid this copy, we do assignments directly. Due to variadic function calls that have special behavior in trailing parts of the assignment, the left to right order was much more intuitive to implement. We implemented conflict detection that makes sure that if the variable on the left hand side is reassigned on the right hand side, we “evacuate” it to a temporary register to keep semantics clean for swaps et al.

I’m not sure off the top of my head if our existing process can be reformed to do right-to-left assignments without losing the efficiency benefits of bypassing a separate copy - it probably can be! But I’m not sure we should.

1 Like

The thread of the post I linked to has more detail regarding possible optimizations.
Since the the order of assignment and evaluation is undefined, the best solution is to optimize for performance. It was my mistake to assume that the behavior was defined.

1 Like

Here is a better example of the order being left to right instead of right to left in luau:

local mt = {
	__newindex = function(self)
		print(self.s..self.s)
	end
}
local function t(str)
	print(str)
	return setmetatable({s=str},mt)
end
t"a".a,t"b".b = t"c",t"d"

In Lua:

a
b
c
d
bb
aa

In Luau:

a
b
c
d
aa
bb

I want to point out that the manual does state that “The assignment statement first evaluates all its expressions and only then are the assignments performed”. (It just doesn’t specify which order the expressions are evaluated, or assignments performed, amongst themselves.) Are there situations other than simple swaps where deviation from this is observable in Luau?

My memory was faulty here - apparently we do evaluate the right hand side into the temporary registers for multiple assignments :frowning: it’s just that our assignment handling for the left hand side is different from vanilla Lua.

Also noteworthy is that the assignment order isn’t as simple as just “right to left” in that there’s subtle handling of cases where on the left hand side, the value is simultaneously assigned-to and used, e.g.

a.foo, a = 1, 2

In this case Lua doesn’t just do assignments from right to left - that would result in a.foo assignment failing.

1 Like