Skip to content
Draft
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
7 changes: 7 additions & 0 deletions Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,13 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
}
break;

case ZEND_HAS_TYPE:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* Variable will be deleted later by FREE, so we can't optimize it */
Tsource[VAR_NUM(opline->op1.var)] = NULL;
}
break;
}

/* get variable source */
Expand Down
6 changes: 5 additions & 1 deletion Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
for (uint32_t i = 0; i < op_array->last_literal; i++) {
if (!info[i].num_related) {
/* unset literal */
zval_ptr_dtor_nogc(&op_array->literals[i]);
if (Z_TYPE(op_array->literals[i]) == IS_TYPE) {
zend_type_release(*(zend_type*)Z_PTR_P(&op_array->literals[i]), false);
} else {
zval_ptr_dtor_nogc(&op_array->literals[i]);
}
continue;
}
switch (Z_TYPE(op_array->literals[i])) {
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/dce.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ static inline bool may_have_side_effects(
case ZEND_FUNC_GET_ARGS:
case ZEND_ARRAY_KEY_EXISTS:
case ZEND_COPY_TMP:
case ZEND_HAS_TYPE:
/* No side effects */
return false;
case ZEND_FREE:
Expand Down
25 changes: 25 additions & 0 deletions Zend/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,31 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
ssa_op++;
SET_RESULT_BOT(op1);
break;
case ZEND_HAS_TYPE: {
zend_ssa *ssa = ctx->scdf.ssa;
zend_ssa_var_info *info = &ssa->var_info[ssa_op->result_def];
if (info->type == MAY_BE_TRUE) {
ZVAL_TRUE(&zv);
SET_RESULT(result, &zv);
return;
} else if (info->type == MAY_BE_FALSE) {
ZVAL_FALSE(&zv);
SET_RESULT(result, &zv);
return;
}

if (!IS_BOT(op1)) {
SKIP_IF_TOP(op1);
zend_type *type = Z_PTR_P(op2);
// FIXME: Abusing internal/return type flags to achieve strict type check
ZVAL_BOOL(&zv, zend_check_type_ex(type, op1, NULL, true, true));
SET_RESULT(result, &zv);
return;
}

SET_RESULT_BOT(result);
return;
}
}

if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
Expand Down
7 changes: 7 additions & 0 deletions Zend/Optimizer/zend_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ void zend_dump_const(const zval *zv)
case IS_ARRAY:
fprintf(stderr, " array(...)");
break;
case IS_TYPE: {
zend_type *type = Z_PTR_P(zv);
zend_string *type_str = zend_type_to_string(*type);
fprintf(stderr, " type(%s)", ZSTR_VAL(type_str));
zend_string_release(type_str);
break;
}
default:
fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
break;
Expand Down
24 changes: 24 additions & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -4042,6 +4042,29 @@ static zend_always_inline zend_result _zend_update_type_info(
case ZEND_FETCH_GLOBALS:
UPDATE_SSA_TYPE(MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def);
break;
case ZEND_HAS_TYPE: {
t1 &= MAY_BE_ANY;
if (t1 & MAY_BE_UNDEF) {
t1 |= MAY_BE_NULL;
}

zend_type *type = Z_PTR_P(CRT_CONSTANT(opline->op2));
uint32_t expected = ZEND_TYPE_PURE_MASK(*type);
if (ZEND_TYPE_HAS_NAME(*type)) {
// FIXME: Implement
UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def);
break;
}

if (!(t1 & ~expected)) {
UPDATE_SSA_TYPE(MAY_BE_TRUE, ssa_op->result_def);
} else if (!(expected & ~t1)) {
UPDATE_SSA_TYPE(MAY_BE_FALSE, ssa_op->result_def);
} else {
UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def);
}
break;
}
default:
#ifdef ZEND_DEBUG_TYPE_INFERENCE
if (ssa_op->result_def >= 0) {
Expand Down Expand Up @@ -5033,6 +5056,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_COPY_TMP:
case ZEND_JMP_NULL:
case ZEND_JMP_FRAMELESS:
case ZEND_HAS_TYPE:
return 0;
case ZEND_IS_IDENTICAL:
case ZEND_IS_NOT_IDENTICAL:
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ static zend_always_inline uint32_t _const_op_type(const zval *zv) {
return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
return zend_array_type_info(zv);
} else if (Z_TYPE_P(zv) == IS_TYPE) {
return MAY_BE_ANY;
} else {
uint32_t tmp = (1 << Z_TYPE_P(zv));

Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/pattern_matching/is/and.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
And pattern
--FILE--
<?php

interface A {}
interface B {}
interface C {}
class Foo implements A, B {}

var_dump(1 is int & 1);
var_dump(2 is int & (1|2));
var_dump(3 is float & 1);
var_dump(4 is int & float);
var_dump([] is [] & [...]);
var_dump('foo' is string & 'bar');
var_dump(new Foo() is A&B);
var_dump(new Foo() is (A&C));

?>
--EXPECT--
bool(true)
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)
30 changes: 30 additions & 0 deletions Zend/tests/pattern_matching/is/array.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Array pattern
--FILE--
<?php

var_dump([] is []);
var_dump(42 is []);
var_dump('foo' is []);
var_dump([42] is [42]);
var_dump([42] is []);
var_dump([42] is [43]);
var_dump([42] is ['0' => 42]);
var_dump([42, 43] is [42]);
var_dump([42, 43] is [42, ...]);
var_dump([42] is [$a]);
var_dump($a);

?>
--EXPECT--
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
int(42)
10 changes: 10 additions & 0 deletions Zend/tests/pattern_matching/is/array_mixed_keys.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Array pattern with mixed implicit and explicit keys
--FILE--
<?php

var_dump([] is ['foo', 1 => 'bar']);

?>
--EXPECTF--
Fatal error: Must not mix implicit and explicit array keys in array pattern in %s on line %d
32 changes: 32 additions & 0 deletions Zend/tests/pattern_matching/is/bail.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
Object pattern matching
--FILE--
<?php

(function () {
$o = new stdClass();

try {
var_dump($o is self);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}

try {
var_dump($o is parent);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}

try {
var_dump($o is static);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
})();

?>
--EXPECT--
Error: Cannot access "self" when no class scope is active
Error: Cannot access "parent" when no class scope is active
Error: Cannot access "static" when no class scope is active
87 changes: 87 additions & 0 deletions Zend/tests/pattern_matching/is/binding.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
--TEST--
Binding pattern
--FILE--
<?php

class Box {
public function __construct(
public $value,
) {}
}

class NotBox {
public function __construct(
public $value,
) {}
}

class Many {
public function __construct(
public $a = 1,
public $b = 2,
public $c = 3,
public $d = 4,
public $e = 5,
public $f = 6,
public $g = 7,
public $h = 8,
public $i = 9,
public $j = 10,
) {}
}

var_dump(10 is $a);
var_dump($a);

var_dump('Hello world' is $a);
var_dump($a);

var_dump(new Box(42) is Box(value: $a));
var_dump($a);

var_dump(new NotBox(43) is Box(value: $a));
var_dump($a);

var_dump(43 is $a & int);
var_dump($a);

var_dump([] is $a & string);
var_dump($a);

var_dump(new Many() is Many(:$a, :$b, :$c, :$d));
var_dump($a, $b, $c, $d, isset($e));

var_dump(new Many() is Many(:$a, :$b, :$c, :$d, :$e, :$f, :$g, :$h, :$i, :$j));
var_dump($a, $b, $c, $d, $e, $f, $g, $h, $i, $j);

?>
--EXPECT--
bool(true)
int(10)
bool(true)
string(11) "Hello world"
bool(true)
int(42)
bool(false)
int(42)
bool(true)
int(43)
bool(false)
int(43)
bool(true)
int(1)
int(2)
int(3)
int(4)
bool(false)
bool(true)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
28 changes: 28 additions & 0 deletions Zend/tests/pattern_matching/is/binding_destruct.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Object pattern matching destructor
--FILE--
<?php

class Foo {
public function __destruct() {
throw new Exception('Here');
}
}

$foo = new Foo();
$bar = 'bar';

try {
42 is $foo & $bar;
} catch (Exception $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}

var_dump($foo);
var_dump($bar);

?>
--EXPECT--
Exception: Here
int(42)
string(3) "bar"
8 changes: 8 additions & 0 deletions Zend/tests/pattern_matching/is/binding_in_or_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
Must not bind to variables in | pattern
--FILE--
<?php
var_dump(42 is $foo|$bar);
?>
--EXPECTF--
Fatal error: Must not bind to variables inside | pattern in %s on line %d
8 changes: 8 additions & 0 deletions Zend/tests/pattern_matching/is/binding_in_or_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
Must not bind to variables in | pattern
--FILE--
<?php
var_dump(42 is 42|(43 & $bar));
?>
--EXPECTF--
Fatal error: Must not bind to variables inside | pattern in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/pattern_matching/is/bug001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Pattern matching: Bug 001
--FILE--
<?php

function test($a) {
$a is 42|43;
}

test(42);

?>
===DONE===
--EXPECT--
===DONE===
10 changes: 10 additions & 0 deletions Zend/tests/pattern_matching/is/bug002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Pattern matching: Bug 002
--FILE--
<?php

var_dump([1, 2, 3] is ([1, 2]));

?>
--EXPECT--
bool(false)
Loading