fix(php): 解决函数返回引用类型的编译错误

- 添加对函数返回引用类型的检查,禁止返回引用类型
- 修复赋值引用表达式的解析逻辑,确保左侧为变量
- 添加临时变量处理非变量表达式的引用赋值
- 新增函数返回引用的测试用例验证错误处理
pull/1/head
韩天峰 5 months ago
parent ecb7795243
commit 8670df2a3e
  1. 15
      phpunit/code/function-return-ref.php
  2. 9
      phpunit/src/FunctionTest.php
  3. 59
      src/Php/CompilerBase.php

@ -0,0 +1,15 @@
<?php
function &test() {
global $a;
return $a;
}
function main()
{
$a = null;
$b = &test();
$b ??= 2;
var_dump($a, $b);
}

@ -0,0 +1,9 @@
<?php
class FunctionTest extends \BaseTest
{
public function testReturnRef()
{
$this->exec('The return type of the function `test` cannot be a reference type', 'function-return-ref.php');
}
}

@ -794,7 +794,11 @@ class CompilerBase extends \PhpAot\Core\Translator
{
// .stub 存根定义 C++ Native 函数,必须设置返回值类型
if (!$v->returnType && $this->stubFile) {
throw new \Exception('No return type for ' . $v->name);
$this->fatalError($v, 'The return type of the function `' . $v->name . '` must be specified');
}
// 返回值不能是引用类型
if ($v->byRef) {
$this->fatalError($v, 'The return type of the function `' . $v->name . '` cannot be a reference type');
}
$fnName = $this->parseIdentifier($v->name);
@ -3739,33 +3743,38 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseAssignRef(Node\Expr\AssignRef $expr): string
{
if ($this->isVarExpr($expr->var)) {
$this->context->inAssignExpr = true;
$left = $this->parseIdentifier($expr->var);
$this->context->inAssignExpr = false;
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
} else {
$type = $this->getVarType($left);
if ($type !== self::TYPE_REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
}
}
if ($this->isVarExpr($expr->expr)) {
return $left . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()';
}
if ($expr->expr->getType() === self::EXPR_ARRAY_DIM_FETCH) {
return $left . ' = ' . $this->parseIdentifier($expr->expr);
}
if ($this->isPropertyFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
$object = $this->parseExpr($expr->expr->var);
$prop = $this->identifierToStr($expr->expr->name);
if (!$this->isVarExpr($expr->var)) {
$this->fatalError($expr, 'Cannot assign reference to non-variable');
}
return $left . ' = ' . $object . '.attrRef(' . $prop . ')';
$this->context->inAssignExpr = true;
$left = $this->parseIdentifier($expr->var);
$this->context->inAssignExpr = false;
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
} else {
$type = $this->getVarType($left);
if ($type !== self::TYPE_REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
}
}
$this->fatalError($expr, 'Cannot assign reference to non-variable');
if ($this->isVarExpr($expr->expr)) {
return $left . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()';
}
if ($expr->expr->getType() === self::EXPR_ARRAY_DIM_FETCH) {
return $left . ' = ' . $this->parseIdentifier($expr->expr);
}
if ($this->isPropertyFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
$object = $this->parseExpr($expr->expr->var);
$prop = $this->identifierToStr($expr->expr->name);
return $left . ' = ' . $object . '.attrRef(' . $prop . ')';
}
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->expr) . ';';
return $left . ' = ' . $tmpVar . '.toReference()';
}
protected function parseMethodCall(Node\Expr\MethodCall $expr): string

Loading…
Cancel
Save