I discovered this just a few weeks ago myself when I had an occasion to update two refs at the same time like this. I wasn’t sure if it would do what I wanted so I checked the lowering and was happy to discover that it did.
It’s even recursive into nested tuples.
julia> Meta.@lower a.x, b[], (c.y, d[]) = (1,2, (3,4))
:($(Expr(:thunk, CodeInfo(
...
│ (Base.setproperty!)(a, :x, %12)
│ (Base.setindex!)(b, %3)
...
│ (Base.setproperty!)(c, :y, %24)
...
│ (Base.setindex!)(d, %28)
Even more obliquely, it’s even allowed to define limited functions this way:
julia> f(), g(), x+y = 1:3
1:3
julia> f()
1
julia> g()
2
julia> 1+1
3
Of course, we cannot do anything more interesting than constant-valued functions since the RHS is an immediately-evaluated value and not a function body.
Looking at the lisp, it looks like tuple destructuring is implemented exactly as a loop that generates the same code as a sequence of assignments for each thing on the LHS while iterating over the RHS. And I think it’s something you can count on as it’d be an error otherwise. Let’s document it!