Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions src/Parser/SwitchTrait.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,11 +38,29 @@ protected function parseSwitch(Node\Stmt\Switch_ $v): string
$var_def .= $type . ' ' . $tmp_var . ' = ' . $condExpr . ';' . PHP_EOL;
$var_def .= $this->formatCapturedStmtLines($condAfterStmts);

// Save the scope; switch parsing may fail partway and add variables in the process, so it must be reset
$localVars = $this->context->localVars;
$code = $this->parseBeforeStmtLines() . PHP_EOL;
$code = $this->parseBeforeStmtLines() . PHP_EOL;

if ($type === Type::INT or $type === Type::BOOL) {
// Check all labels before lowering any bodies. C++ requires integer
// labels; for bool subjects, only 0 and 1 preserve PHP comparison.
$nativeCaseValues = [];
foreach ($v->cases as $case) {
$caseCond = $case->cond;
if ($caseCond === null) {
continue;
}
if (!$caseCond instanceof Node\Scalar\Int_
|| ($type === Type::BOOL && $caseCond->value !== 0 && $caseCond->value !== 1)
) {
goto _fail;
}
if (isset($nativeCaseValues[$caseCond->value])) {
// PHP permits duplicate case values and selects the first
// matching label, while C++ rejects duplicate case labels.
goto _fail;
}
$nativeCaseValues[$caseCond->value] = true;
}
$code .= 'do {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'switch (' . $tmp_var . ') {' . PHP_EOL;
Expand All@@ -51,12 +69,6 @@ protected function parseSwitch(Node\Stmt\Switch_ $v): string
if (empty($case->cond)) {
$code .= $this->getIndent() . 'default: {' . PHP_EOL;
} else {
$condType = $case->cond->getType();
if ($condType !== 'Scalar_Int' and $condType !== 'Scalar_Float') {
$this->context->localVars = $localVars;
$this->indentLevel -= 2;
goto _fail;
}
$code .= $this->getIndent() . 'case ' . $this->parseScalar($case->cond) . ': {' . PHP_EOL;
}
$code .= $this->parseBlockStmts($case->stmts);
Expand DownExpand Up@@ -135,6 +147,9 @@ protected function parseSwitch(Node\Stmt\Switch_ $v): string
$code .= $this->formatCapturedStmtLines($caseAfterStmts);
$caseCondExpr = $caseTmpVar;
}
if ($type === Type::BOOL) {
$caseCondExpr = 'php::toBool(' . $caseCondExpr . ')';
}
$code .= $this->getIndent() . $groupMatched . ' = php::equals(' . $tmp_var . ', ' . $caseCondExpr . ');' . PHP_EOL;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
Expand Down
40 changes: 40 additions & 0 deletions tests/compiler/switch/bool-numeric-case.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
--TEST--
Boolean switch subjects use PHP loose comparison for numeric cases
--FILE--
<?php
function select_bool(bool $value): string
{
switch ($value) {
case 2:
return 'nonzero';
case 0:
return 'zero';
default:
return 'default';
}
}

function select_binary_bool(bool $value): string
{
switch ($value) {
case 0:
return 'false';
case 1:
return 'true';
}
return 'default';
}

function main(): void
{
echo select_bool(true), "\n";
echo select_bool(false), "\n";
echo select_binary_bool(true), "\n";
echo select_binary_bool(false), "\n";
}
?>
--EXPECT--
nonzero
zero
true
false
41 changes: 41 additions & 0 deletions tests/compiler/switch/duplicate-integer-case.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Duplicate integer switch cases retain PHP first-match semantics
--FILE--
<?php
function select_duplicate_int(int $value): string
{
switch ($value) {
case 1:
return 'first-int';
case 1:
return 'second-int';
default:
return 'default-int';
}
}

function select_duplicate_bool(bool $value): string
{
switch ($value) {
case 1:
return 'first-bool';
case 1:
return 'second-bool';
default:
return 'default-bool';
}
}

function main(): void
{
echo select_duplicate_int(1), "\n";
echo select_duplicate_int(2), "\n";
echo select_duplicate_bool(true), "\n";
echo select_duplicate_bool(false), "\n";
}
?>
--EXPECT--
first-int
default-int
first-bool
default-bool
29 changes: 29 additions & 0 deletions tests/compiler/switch/int-float-case.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
Integer switch subjects accept floating-point case labels
--FILE--
<?php
function select_number(int $value): string
{
switch ($value) {
case 0:
return 'zero';
case 1.5:
return 'fraction';
case 2.0:
return 'two';
default:
return 'default';
}
}

function main(): void
{
echo select_number(0), "\n";
echo select_number(1), "\n";
echo select_number(2), "\n";
}
?>
--EXPECT--
zero
default
two
Loading