diff --git a/phpunit/code/native-condition-temporaries.php b/phpunit/code/native-condition-temporaries.php new file mode 100644 index 00000000..2233f2d8 --- /dev/null +++ b/phpunit/code/native-condition-temporaries.php @@ -0,0 +1,25 @@ +addFiles([$source]); + $compiler->prepareFile($source); + $code = file_get_contents($compiler->convertFile($source)); + + preg_match_all('/(tmp_var_\d+) = php::toBool\(php::same\(/', $code, $matches); + self::assertCount(2, $matches[1]); + foreach ($matches[1] as $temporary) { + self::assertStringContainsString('php::Bool ' . $temporary . ' = 0;', $code); + } + + $dynamicBody = explode('php::Bool php_dynamiccondition(', $code, 2)[1]; + $dynamicBody = explode("\n}", $dynamicBody, 2)[0]; + self::assertStringContainsString('php::Var tmp_var_1;', $dynamicBody); + self::assertStringContainsString('tmp_var_1 = php_dynamicconditionvalue(', $dynamicBody); + } +} diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 38b708ef..d4903e24 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -1984,7 +1984,13 @@ protected function genConditionWithCapturedStmts(NodeAbstract $cond, string $ope $code = ''; $code .= $this->formatCapturedStmtLines($beforeStmts); if ($afterStmts) { - $tmpVar = $this->addTmpVar(Type::VAR); + // Boolean results own no zval resources and can survive operand + // cleanup in native storage without changing evaluation order. + $type = $this->detectTypeOfExpr($cond) === Type::BOOL ? Type::BOOL : Type::VAR; + $tmpVar = $this->addTmpVar($type); + if ($type === Type::BOOL) { + $condExpr = $this->convertBoolExpr($condExpr); + } $code .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL; $code .= $this->formatCapturedStmtLines($afterStmts); $condExpr = $tmpVar; @@ -4501,6 +4507,9 @@ protected function genStaticVarInitLambda(Node\Stmt\StaticVar $var, string $varN $this->context = new FunctionContext(); $this->context->arguments = $oriCtx->localVars; + // Outer locals are captured arguments. New initializer temporaries + // must not reuse their names and inherit an incompatible scalar type. + $this->context->tmpVarIndex = $oriCtx->tmpVarIndex; $code = '([&](){' . PHP_EOL; $body = $this->getIndent() . $varName . ' = ' . $this->parseExpr($var->default) . ';'; diff --git a/src/Parser/BinaryOpTrait.php b/src/Parser/BinaryOpTrait.php index ab735c59..a87657fa 100644 --- a/src/Parser/BinaryOpTrait.php +++ b/src/Parser/BinaryOpTrait.php @@ -1275,7 +1275,11 @@ protected function parseShortCircuitLogicalOp(NodeAbstract $left, NodeAbstract $ $this->indentLevel++; $code .= $this->formatCapturedStmtLines($rightBeforeStmts); if ($rightAfterStmts) { - $rightTmpVar = $this->addTmpVar(Type::VAR); + $type = $this->detectTypeOfExpr($right) === Type::BOOL ? Type::BOOL : Type::VAR; + $rightTmpVar = $this->addTmpVar($type); + if ($type === Type::BOOL) { + $rightExpr = $this->convertBoolExpr($rightExpr); + } $code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';' . PHP_EOL; $code .= $this->formatCapturedStmtLines($rightAfterStmts); $rightExpr = $rightTmpVar; diff --git a/tests/compiler/optimizations/native-condition-static-initializer.phpt b/tests/compiler/optimizations/native-condition-static-initializer.phpt new file mode 100644 index 00000000..83d3a152 --- /dev/null +++ b/tests/compiler/optimizations/native-condition-static-initializer.phpt @@ -0,0 +1,14 @@ +--TEST-- +Static initializer temporaries do not reuse outer native condition snapshots +--FILE-- + +--EXPECT-- +value +value