midorikocak · GitHub

@passcod Quoting part of a mail I wrote in one of the discussion threads for this feature:

The only statement the RFC essentially makes is that $a ??= $b will be the same as $a = $a ?? $b, for variable-expression $a and expression $b. This statement, while a good high-level illustration, does not explain the exact behavior of this operator.

For example, consider the expression $a[print 'X'] ??= $b. A simple desugaring into $a[print 'X'] = $a[print 'X'] ?? $b will result in 'X' being printed twice. However, this is not how all other existing compound assignment operators behave: They will print X only once, as the LHS is only evaluated once. I assume that ??= would behave the same way.

However, with ??= the problem becomes more complicated. Let us assume that $a is an ArrayAccess object and consider the expression $a[0] ??= $b. Let us further assume that $x = $a->offsetGet(0) is non-null. Will $a[0] ??= $b result in a call to $a->offsetSet(0, $x)? This is what would normally happen with a compound assignment operator and what would be implied by the desugaring $a[0] = $a[0] ?? $b. However this assignment is not really necessary, as we're just reassigning the same value. So, does the call happen or not? Is the proper desugaring maybe if (!isset($a[0])) $a[0] = $b?

Let us now assume that $a is a recursive ArrayAccess object with by-reference offsetGet() and consider the expression $a[0][1] ??= expr. For a normal compound assignment operator, this would issue the call sequence

$b = expr;
$x =& $a->offsetGet(0);
$y = $x->offsetGet(1);
$y OP= $b;
$x->offsetSet(1, $y);

Note that we only issue one offsetSet() at the end. We do not refetch $x via $a->offsetGet(0). How would the same work with the ??= operator? As the RHS is evaluated lazily, it is my opinion that only performing the offsetSet() call without refetching $x beforehand would violate PHP's indirection memory model. Additionally as ??= has to fetch offsets in BP_VAR_IS mode, we likely wouldn't be able to write them without refetching anymore.

So, what would be the desugared call sequence for $a[0][1] ??= expr? Something like this?

if (!$a->offsetHas(0)) {
    goto assign;
}
$x = $a->offsetGet(0);
if (x === null) {
    goto assign;
}
if (!$x->offsetHas(0)) {
    goto assign;
}
$y = $x->offsetGet(0);
if ($y === null) {
    goto assign;
}
goto done;
assign:
$b = expr;
$x =& $a->offsetGet(0);
$x->offsetSet(1, $b);
done:

That would be some first thoughts on the issue, though I'm sure there are more subtleties involved. I'd like to see the exact behavior of ??= (and ?:=) specified.

I'm also pretty sure that writing a patch for this will not be entirely easy. The combination of execute-once LHS side-effects and lazy RHS execution does not translate well to PHP's VM constraints.

The last line is the TL;DR, those are the issues that need to be solved. Additionally there is the question mentioned earlier where $a ??= $b is approximately $a = $a ?? $b or $a ?? $a = $b (the latter I believe has precedent in Ruby).

Read the original on github.com ↗