Merge branch 'master' of git.code-galaxy.net:aot/compiler

pull/43/head
韩天峰 4 weeks ago
commit 6ecc447735
  1. 16
      phpunit/code/bool-literal-identical.php
  2. 53
      phpunit/code/control-flow/loop-switch-continue.php
  3. 11
      phpunit/code/control-flow/standalone-dynamic-switch-continue.php
  4. 13
      phpunit/code/control-flow/standalone-switch-continue.php
  5. 25
      phpunit/src/LoopControlTest.php
  6. 20
      phpunit/src/OperatorTest.php
  7. 9
      src/CompilerBase.php
  8. 5
      src/Context/FunctionContext.php
  9. 5
      src/Parser/AstNodeType.php
  10. 18
      src/Parser/BinaryOpTrait.php
  11. 2
      src/Parser/LoopControlTrait.php
  12. 3
      src/Parser/SwitchTrait.php
  13. 34
      tests/compiler/operator/bool-literal-identical.phpt
  14. 2
      version.txt

@ -0,0 +1,16 @@
<?php
function bool_literal_identical(bool $pjax): array
{
return [
true === $pjax,
$pjax === true,
false !== $pjax,
$pjax !== false,
];
}
function dynamic_bool_literal_identical(mixed $value): bool
{
return true === $value;
}

@ -0,0 +1,53 @@
<?php
use native_types;
function switch_continue_in_for(): void
{
for ($i = 0; $i < 2; $i++) {
switch ($i) {
case 0:
continue;
default:
break;
}
}
}
function switch_continue_in_foreach(array $values): void
{
foreach ($values as $value) {
switch ($value) {
case 0:
continue;
default:
break;
}
}
}
function switch_continue_in_while(): void
{
$i = 0;
while ($i++ < 2) {
switch ($i) {
case 1:
continue;
default:
break;
}
}
}
function switch_continue_in_do_while(): void
{
$i = 0;
do {
switch ($i++) {
case 0:
continue;
default:
break;
}
} while ($i < 2);
}

@ -0,0 +1,11 @@
<?php
function standalone_dynamic_switch_continue(mixed $value): void
{
switch ($value) {
case 1:
continue;
default:
break;
}
}

@ -0,0 +1,13 @@
<?php
use native_types;
function standalone_switch_continue(int $value): void
{
switch ($value) {
case 1:
continue;
default:
break;
}
}

@ -0,0 +1,25 @@
<?php
class LoopControlTest extends \BaseTest
{
public function testContinueInStandaloneSwitchIsRejected(): void
{
$this->exec(
'Cannot continue outside loop',
'control-flow/standalone-switch-continue.php',
);
}
public function testContinueInStandaloneDynamicSwitchIsRejected(): void
{
$this->exec(
'Cannot continue outside loop',
'control-flow/standalone-dynamic-switch-continue.php',
);
}
public function testContinueInSwitchNestedInLoopsIsAllowed(): void
{
$this->compile('control-flow/loop-switch-continue.php');
}
}

@ -2,6 +2,26 @@
class OperatorTest extends \BaseTest
{
public function testBooleanLiteralStrictComparisonUsesNativeBoolOperands(): void
{
global $translator;
$compiler = \TypePhp\CompilerTest::create(ROOT_PATH);
$translator = $compiler;
$testFile = __DIR__ . '/../code/bool-literal-identical.php';
$compiler->addFiles([$testFile]);
$compiler->prepareFile($testFile);
$cppFile = $compiler->convertFile($testFile);
$cpp = file_get_contents($cppFile);
$this->assertStringContainsString('true == pjax', $cpp);
$this->assertStringContainsString('pjax == true', $cpp);
$this->assertStringContainsString('false == pjax', $cpp);
$this->assertStringContainsString('pjax == false', $cpp);
$this->assertStringNotContainsString('php::true_ == pjax', $cpp);
$this->assertStringNotContainsString('php::false_ == pjax', $cpp);
$this->assertStringContainsString('php::same(php::true_, value)', $cpp);
}
public function testLiteralIntDivideByZeroDoesNotCompile(): void
{
$this->exec('Cannot divide or modulo by zero', 'divide-by-zero-int.php');

@ -1516,6 +1516,7 @@ class CompilerBase implements PropertyAccessContext
$this->context->enterScope();
$lines = [];
$inLoopTop = $this->context->inLoop;
$inContinuableLoopTop = $this->context->inContinuableLoop;
$last = array_key_last($stmts);
foreach ($stmts as $i => $v) {
$class = $v->getType();
@ -1545,13 +1546,17 @@ class CompilerBase implements PropertyAccessContext
break;
case 'Stmt_For':
$this->context->inLoop = true;
$this->context->inContinuableLoop = true;
$result = $this->parseFor($v);
$this->context->inLoop = $inLoopTop;
$this->context->inContinuableLoop = $inContinuableLoopTop;
break;
case 'Stmt_Foreach':
$this->context->inLoop = true;
$this->context->inContinuableLoop = true;
$result = $this->parseForeach($v);
$this->context->inLoop = $inLoopTop;
$this->context->inContinuableLoop = $inContinuableLoopTop;
break;
case 'Stmt_Switch':
$this->context->inLoop = true;
@ -1560,13 +1565,17 @@ class CompilerBase implements PropertyAccessContext
break;
case 'Stmt_While':
$this->context->inLoop = true;
$this->context->inContinuableLoop = true;
$result = $this->parseWhile($v);
$this->context->inLoop = $inLoopTop;
$this->context->inContinuableLoop = $inContinuableLoopTop;
break;
case 'Stmt_Do':
$this->context->inLoop = true;
$this->context->inContinuableLoop = true;
$result = $this->parseDo($v);
$this->context->inLoop = $inLoopTop;
$this->context->inContinuableLoop = $inContinuableLoopTop;
break;
case 'Stmt_If':
$result = $this->parseIf($v);

@ -55,7 +55,10 @@ class FunctionContext
public array $ceWrappers = [];
public int $tmpVarIndex = 0;
public array $arguments = [];
/** True while parsing a breakable loop or switch. */
public bool $inLoop = false;
/** True while parsing a for/foreach/while/do-while body. */
public bool $inContinuableLoop = false;
public bool $inClosure = false;
public ?array $closureReturnTypeCheck = null;
public string $closureReturnTypeStr = '';
@ -97,6 +100,7 @@ class FunctionContext
$this->scopeLayouts = [];
$this->scopeLevel = 0;
$this->inLoop = false;
$this->inContinuableLoop = false;
$this->inClosure = false;
$this->closureReturnTypeCheck = null;
$this->closureReturnTypeStr = '';
@ -127,5 +131,6 @@ class FunctionContext
$this->scopeLayouts = [];
$this->scopeLevel = 0;
$this->inLoop = false;
$this->inContinuableLoop = false;
}
}

@ -152,6 +152,11 @@ trait AstNodeType
return $expr instanceof Node\Stmt\Break_;
}
protected function isContinueExpr(NodeAbstract $expr): bool
{
return $expr instanceof Node\Stmt\Continue_;
}
protected function isThrowExpr(NodeAbstract $expr): bool
{
if ($expr instanceof Node\Stmt\Expression) {

@ -414,11 +414,29 @@ trait BinaryOpTrait
return null;
}
if ($leftType === $rightType) {
if ($leftType === Type::BOOL) {
$cppLeft = $this->nativeBoolLiteral($astLeft) ?? $cppLeft;
$cppRight = $this->nativeBoolLiteral($astRight) ?? $cppRight;
}
return $cppLeft . ' == ' . $cppRight;
}
return 'false';
}
/**
* Strict comparisons between native booleans must use C++ bool literals.
* parseCompareExpr() normally emits php::true_/php::false_ Variants because
* dynamic comparisons need zvals, but those wrappers are incorrect once
* optimizeIdenticalOp() selects a direct primitive comparison.
*/
private function nativeBoolLiteral(NodeAbstract $expr): ?string
{
if (!$this->isScalarBool($expr)) {
return null;
}
return strcasecmp($expr->name->toString(), 'true') === 0 ? 'true' : 'false';
}
protected function parseBinaryOpLogicalAnd(Expr\BinaryOp\LogicalAnd|Expr\BinaryOp\BooleanAnd $expr): string
{
return $this->parseShortCircuitLogicalOp($expr->left, $expr->right, '&&');

@ -183,7 +183,7 @@ trait LoopControlTrait
protected function parseContinue(Node\Stmt\Continue_ $v): string
{
if (!$this->context->inLoop) {
if (!$this->context->inContinuableLoop) {
$this->fatalError($v, 'Cannot continue outside loop');
}
$num = $v->num;

@ -90,9 +90,10 @@ trait SwitchTrait
if (!$this->isReturnExpr($lastExpr)
and !$this->isExitExpr($lastExpr)
and !$this->isBreakExpr($lastExpr)
and !$this->isContinueExpr($lastExpr)
and !$this->isThrowExpr($lastExpr)
) {
$this->fatalError($case, 'switch case must end with return/break/exit/throw, ' . $lastExpr->getType() . ' given');
$this->fatalError($case, 'switch case must end with return/break/continue/exit/throw, ' . $lastExpr->getType() . ' given');
}
$target = count($caseGroups);
if ($hasDefault) {

@ -0,0 +1,34 @@
--TEST--
Boolean literals on either side of strict comparisons
--FILE--
<?php
function compare_bool_literal(bool $pjax): void
{
var_dump(true === $pjax);
var_dump($pjax === true);
var_dump(false === $pjax);
var_dump($pjax === false);
var_dump(true !== $pjax);
var_dump($pjax !== true);
}
function main(): void
{
compare_bool_literal(true);
compare_bool_literal(false);
}
?>
--EXPECT--
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)

@ -1 +1 @@
1096
1096

Loading…
Cancel
Save