Skip to content

Commit febcdb8

Browse files
committed
Implement pattern matching
1 parent 20bc059 commit febcdb8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2132
-591
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,13 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
997997
}
998998
}
999999
break;
1000+
1001+
case ZEND_HAS_TYPE:
1002+
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
1003+
/* Variable will be deleted later by FREE, so we can't optimize it */
1004+
Tsource[VAR_NUM(opline->op1.var)] = NULL;
1005+
}
1006+
break;
10001007
}
10011008

10021009
/* get variable source */

Zend/Optimizer/compact_literals.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
276276
for (uint32_t i = 0; i < op_array->last_literal; i++) {
277277
if (!info[i].num_related) {
278278
/* unset literal */
279-
zval_ptr_dtor_nogc(&op_array->literals[i]);
279+
if (Z_TYPE(op_array->literals[i]) == IS_TYPE) {
280+
zend_type_release(*(zend_type*)Z_PTR_P(&op_array->literals[i]), false);
281+
} else {
282+
zval_ptr_dtor_nogc(&op_array->literals[i]);
283+
}
280284
continue;
281285
}
282286
switch (Z_TYPE(op_array->literals[i])) {

Zend/Optimizer/dce.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ static inline bool may_have_side_effects(
125125
case ZEND_FUNC_GET_ARGS:
126126
case ZEND_ARRAY_KEY_EXISTS:
127127
case ZEND_COPY_TMP:
128+
case ZEND_HAS_TYPE:
128129
/* No side effects */
129130
return false;
130131
case ZEND_FREE:

Zend/Optimizer/sccp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,14 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
17641764
SET_RESULT_BOT(result);
17651765
break;
17661766
}
1767+
case ZEND_HAS_TYPE: {
1768+
SKIP_IF_TOP(op1);
1769+
zend_type *type = Z_PTR_P(op2);
1770+
// FIXME: Abusing internal/return type flags to achieve strict type check
1771+
ZVAL_BOOL(&zv, zend_check_type_ex(type, op1, NULL, true, true));
1772+
SET_RESULT(result, &zv);
1773+
break;
1774+
}
17671775
default:
17681776
{
17691777
/* If we have no explicit implementation return BOT */

Zend/Optimizer/zend_inference.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4042,6 +4042,29 @@ static zend_always_inline zend_result _zend_update_type_info(
40424042
case ZEND_FETCH_GLOBALS:
40434043
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);
40444044
break;
4045+
case ZEND_HAS_TYPE: {
4046+
t1 &= MAY_BE_ANY;
4047+
if (t1 & MAY_BE_UNDEF) {
4048+
t1 |= MAY_BE_NULL;
4049+
}
4050+
4051+
zend_type *type = Z_PTR_P(CRT_CONSTANT(opline->op2));
4052+
uint32_t expected = ZEND_TYPE_PURE_MASK(*type);
4053+
if (ZEND_TYPE_HAS_NAME(*type)) {
4054+
// FIXME: Implement
4055+
UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def);
4056+
break;
4057+
}
4058+
4059+
if (!(t1 & ~expected)) {
4060+
UPDATE_SSA_TYPE(MAY_BE_TRUE, ssa_op->result_def);
4061+
} else if (!(expected & ~t1)) {
4062+
UPDATE_SSA_TYPE(MAY_BE_FALSE, ssa_op->result_def);
4063+
} else {
4064+
UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def);
4065+
}
4066+
break;
4067+
}
40454068
default:
40464069
#ifdef ZEND_DEBUG_TYPE_INFERENCE
40474070
if (ssa_op->result_def >= 0) {
@@ -5033,6 +5056,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
50335056
case ZEND_COPY_TMP:
50345057
case ZEND_JMP_NULL:
50355058
case ZEND_JMP_FRAMELESS:
5059+
case ZEND_HAS_TYPE:
50365060
return 0;
50375061
case ZEND_IS_IDENTICAL:
50385062
case ZEND_IS_NOT_IDENTICAL:

Zend/Optimizer/zend_inference.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ static zend_always_inline uint32_t _const_op_type(const zval *zv) {
150150
return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
151151
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
152152
return zend_array_type_info(zv);
153+
} else if (Z_TYPE_P(zv) == IS_TYPE) {
154+
return MAY_BE_ANY;
153155
} else {
154156
uint32_t tmp = (1 << Z_TYPE_P(zv));
155157

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
And pattern
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface C {}
9+
class Foo implements A, B {}
10+
11+
var_dump(1 is int & 1);
12+
var_dump(2 is int & (1|2));
13+
var_dump(3 is float & 1);
14+
var_dump(4 is int & float);
15+
var_dump([] is [] & [...]);
16+
var_dump('foo' is string & 'bar');
17+
var_dump(new Foo() is A&B);
18+
var_dump(new Foo() is (A&C));
19+
20+
?>
21+
--EXPECT--
22+
bool(true)
23+
bool(true)
24+
bool(false)
25+
bool(false)
26+
bool(true)
27+
bool(false)
28+
bool(true)
29+
bool(false)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Array pattern
3+
--FILE--
4+
<?php
5+
6+
var_dump([] is []);
7+
var_dump(42 is []);
8+
var_dump('foo' is []);
9+
var_dump([42] is [42]);
10+
var_dump([42] is []);
11+
var_dump([42] is [43]);
12+
var_dump([42] is ['0' => 42]);
13+
var_dump([42, 43] is [42]);
14+
var_dump([42, 43] is [42, ...]);
15+
var_dump([42] is [$a]);
16+
var_dump($a);
17+
18+
?>
19+
--EXPECT--
20+
bool(true)
21+
bool(false)
22+
bool(false)
23+
bool(true)
24+
bool(false)
25+
bool(false)
26+
bool(true)
27+
bool(false)
28+
bool(true)
29+
bool(true)
30+
int(42)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Array pattern with mixed implicit and explicit keys
3+
--FILE--
4+
<?php
5+
6+
var_dump([] is ['foo', 1 => 'bar']);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Must not mix implicit and explicit array keys in array pattern in %s on line %d
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Object pattern matching
3+
--FILE--
4+
<?php
5+
6+
(function () {
7+
$o = new stdClass();
8+
9+
try {
10+
var_dump($o is self);
11+
} catch (Throwable $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
15+
try {
16+
var_dump($o is parent);
17+
} catch (Throwable $e) {
18+
echo $e->getMessage(), "\n";
19+
}
20+
21+
try {
22+
var_dump($o is static);
23+
} catch (Throwable $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
})();
27+
28+
?>
29+
--EXPECT--
30+
Cannot access "self" when no class scope is active
31+
Cannot access "parent" when no class scope is active
32+
Cannot access "static" when no class scope is active

0 commit comments

Comments
 (0)