fix(compiler): preserve ref snapshot evaluation order

pull/23/head
韩天峰 1 month ago
parent 6daa312183
commit 1128fb06c8
  1. 17
      src/CompilerBase.php
  2. 34
      tests/compiler/ref/dynamic-return-reference-argument.phpt

@ -1067,23 +1067,16 @@ class CompilerBase implements PropertyAccessContext
}
/**
* 把一个"按引用返回"的调用在按值消费处解引用为值快照。
*
* 返回引用的调用会产生一个指向被调用方存储的活引用。当该调用被按值消费
* (例如作为按值函数参数、数组元素、按值返回等会触发 PHP 分离语义的上下文)
* 时,PHP 会在求值那一刻拷贝出值的快照,因此之后对别名存储的修改不应再可见。
* 我们通过把结果赋值给一个临时 php::Var 来分离引用(普通的 Var 赋值会断开
* 引用,而 php::Variant(php::Ref) 构造会保留引用),从而保留从左到右的求值顺序。
*
* 注意:二元/一元运算等操作数上下文应保持引用活动、在运算时读值,不应在此快照;
* 那些上下文由各自的解析器直接保留引用。
* Snapshot a reference-returning call before a by-value container can retain
* its php::Ref. Assigning to an existing Var detaches the reference, unlike
* constructing a Variant directly from Ref. Keep the assignment inline so
* earlier arguments or array elements retain PHP's evaluation order.
*/
protected function materializeRefReturnAsValue(NodeAbstract $value, string $expr): string
{
if ($value instanceof Expr\CallLike && $this->resolveRefReturningCall($value) !== false) {
$tmpVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $expr . ';';
return $tmpVar;
return '(' . $tmpVar . ' = ' . $expr . ')';
}
return $expr;
}

@ -14,6 +14,9 @@ function main()
var_dump($v1, $v2);
var_dump(test1(), test2());
var_dump([test1(), test2()]);
var_dump(['first' => test1(), test2()]);
var_dump(value_order('arg-left'), ref_order('arg-ref'));
var_dump([value_order('array-left'), ref_order('array-ref')]);
}
function &test1()
@ -28,6 +31,19 @@ function &test2()
++$value;
return $value;
}
function value_order(string $label): string
{
echo "$label\n";
return $label;
}
function &ref_order(string $label)
{
static $value = 42;
echo "$label\n";
return $value;
}
?>
--EXPECT--
int(1)
@ -44,3 +60,21 @@ array(2) {
[1]=>
int(4)
}
array(2) {
["first"]=>
int(5)
[0]=>
int(6)
}
arg-left
arg-ref
string(8) "arg-left"
int(42)
array-left
array-ref
array(2) {
[0]=>
string(10) "array-left"
[1]=>
int(42)
}

Loading…
Cancel
Save