Tag:
Branch:
Tree:
b61b59f808
master
speed_build
v0.0.1
v0.0.2
v0.0.3
v0.0.4
v0.0.5
v0.0.6
v0.0.7
v0.1.0
v0.4.0
v0.4.1
v0.6.1
v0.6.2
v0.6.5
v0.6.6
v0.6.8
${ noResults }
1 Commits (b61b59f808d3a2053c4f23e8bcc8e94894d6faa5)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
20f0b5a284
|
fix(codegen): evaluate compound ??= RHS only when the target is not set (#47)
* fix(codegen): evaluate compound ??= RHS only when the target is not set
PHP evaluates the right-hand side of ??= lazily: `$a = 1;
$a ??= sideEffect() + 1;` never calls sideEffect(). When the RHS was a
compound expression the compiler materialized its lowered statements
(the call result temporary) into the enclosing statement context, so
the generated C++ executed the side-effecting call unconditionally
before the isset check.
Generalize the conditional-lambda lowering that already protected
native-object targets: whenever the RHS captured before/after
statements, emit an immediately-invoked lambda whose not-set branch
contains those statements, the assignment and the cleanup. The simple
inline form (`$b ??= f()`) keeps its existing conditional-expression
codegen unchanged.
* fix(codegen): stabilize ??= targets and finish the RHS before assigning
Zend evaluates a coalesce-assignment target's receiver and array keys
exactly once, before the isset check and regardless of its outcome; the
string-based lowering mentioned the target on every use (isset, read,
write, returned value), so a side-effecting receiver ran twice when the
target was set and three times when it was not. Side-effecting target
subexpressions are now materialized into temporaries in source order
(array containers keep their original variable — writing through a
copied temporary would write to the copy — while object receivers are
handles) and the rewritten target reuses them everywhere.
The captured branch also assigned the target before running the RHS's
deferred write-backs, so a postfix increment on the RHS finished after
the outer assignment — observable by a set hook on the target. The RHS
now completes into a temporary (write-backs included) before the target
is written, and the assignment expression itself is returned so the
target is not read again afterwards.
* fix(codegen): stabilize every ??= target subexpression, bound temporary lifetimes
Three target-stabilization gaps in the coalesce-assignment lowering:
- A value-producing array container (makeArray()[keyName()] ??= 42) was
left unstabilized: the dimension was materialized first, reversing
PHP's container-then-key source order, and the container ran once for
the isset check and again for the write. Non-variable containers are
now materialized in source order; plain-variable containers keep
write-through semantics, and a value-producing container is itself
the temporary PHP writes into.
- Dynamic property names were re-evaluated on every mention:
$box->{propertyName()} ran the name expression twice per branch, and
StaticPropertyFetch was not handled at all. Dynamic instance names,
static class expressions and static property names are now
materialized once, in PHP evaluation order (receiver, name, then
dimension), recursively through chained targets.
- The materialized temporaries were function-scoped, deferring the
receiver's destructor to function exit where PHP destroys it at the
end of the statement. Temporaries now follow the established lifetime
idiom (stabilizeAssignOpPropertyReceiver): zval-owning Variants are
.unset() at statement end and Native pointer temporaries reset to
nullptr.
|
20 hours ago |