Skip to content

Commit 1b8fa04

Browse files
committed
feat(zend, opcache, reflection): bound checks and parametric substitution for generics
Signed-off-by: azjezz <[email protected]>
1 parent 87be460 commit 1b8fa04

103 files changed

Lines changed: 2696 additions & 72 deletions

File tree

Some content is hidden

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

Zend/Optimizer/optimize_func_calls.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,46 @@ static void zend_delete_call_instructions(const zend_op_array *op_array, zend_op
7474
}
7575
}
7676

77+
/* Returns true if a VERIFY_GENERIC_ARGUMENTS sits between this call's INIT and
78+
* DO opcodes; such a call cannot be inlined because the verify opcode reads
79+
* EX(call), which goes away once the frame is dropped. */
80+
static bool zend_call_has_generic_arguments_check(zend_op *opline)
81+
{
82+
int call = 0;
83+
while (1) {
84+
switch (opline->opcode) {
85+
case ZEND_INIT_FCALL_BY_NAME:
86+
case ZEND_INIT_NS_FCALL_BY_NAME:
87+
case ZEND_INIT_STATIC_METHOD_CALL:
88+
case ZEND_INIT_METHOD_CALL:
89+
case ZEND_INIT_FCALL:
90+
case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
91+
if (call == 0) {
92+
return false;
93+
}
94+
ZEND_FALLTHROUGH;
95+
case ZEND_NEW:
96+
case ZEND_INIT_DYNAMIC_CALL:
97+
case ZEND_INIT_USER_CALL:
98+
call--;
99+
break;
100+
case ZEND_DO_FCALL:
101+
case ZEND_DO_ICALL:
102+
case ZEND_DO_UCALL:
103+
case ZEND_DO_FCALL_BY_NAME:
104+
call++;
105+
break;
106+
case ZEND_VERIFY_GENERIC_ARGUMENTS:
107+
if (call == 0) {
108+
return true;
109+
}
110+
break;
111+
}
112+
113+
opline--;
114+
}
115+
}
116+
77117
static void zend_try_inline_call(zend_op_array *op_array, const zend_op *fcall, zend_op *opline, const zend_function *func)
78118
{
79119
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
@@ -97,6 +137,11 @@ static void zend_try_inline_call(zend_op_array *op_array, const zend_op *fcall,
97137
return;
98138
}
99139

140+
if (zend_call_has_generic_arguments_check(opline - 1)) {
141+
/* The verify opcode must run; inlining would orphan it. */
142+
return;
143+
}
144+
100145
for (i = 0; i < num_args; i++) {
101146
/* Don't inline functions with by-reference arguments. This would require
102147
* correct handling of INDIRECT arguments. */
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Declaration: type-parameter default that satisfies its bound is accepted
3+
--FILE--
4+
<?php
5+
class A {}
6+
class B extends A {}
7+
8+
class Box<T : A = B> {}
9+
function f<T : A = B>(): void {}
10+
trait Tr<T : A = B> {}
11+
interface I<T : A = B> {}
12+
13+
new Box;
14+
f();
15+
echo "OK\n";
16+
?>
17+
--EXPECT--
18+
OK
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Declaration: class type-parameter default that does not satisfy its bound is rejected
3+
--FILE--
4+
<?php
5+
class Animal {}
6+
class Box<T : Animal = int> {}
7+
?>
8+
--EXPECTF--
9+
Fatal error: Default int for type parameter T does not satisfy its bound Animal in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Declaration: function type-parameter default that does not satisfy its bound is rejected
3+
--FILE--
4+
<?php
5+
class Animal {}
6+
function id<T : Animal = int>(): void {}
7+
?>
8+
--EXPECTF--
9+
Fatal error: Default int for type parameter T does not satisfy its bound Animal in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Declaration: interface type-parameter default that does not satisfy its bound is rejected
3+
--FILE--
4+
<?php
5+
class Animal {}
6+
interface I<T : Animal = int> {}
7+
?>
8+
--EXPECTF--
9+
Fatal error: Default int for type parameter T does not satisfy its bound Animal in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Declaration: trait type-parameter default that does not satisfy its bound is rejected
3+
--FILE--
4+
<?php
5+
class Animal {}
6+
trait Holder<T : Animal = int> {}
7+
?>
8+
--EXPECTF--
9+
Fatal error: Default int for type parameter T does not satisfy its bound Animal in %s on line %d
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--TEST--
2+
Declaration: default checked against intersection bound when types are concrete
3+
--FILE--
4+
<?php
5+
class Box<T : Traversable & Countable = int> {}
6+
?>
7+
--EXPECTF--
8+
Fatal error: Default int for type parameter T does not satisfy its bound Traversable&Countable in %s on line %d
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--TEST--
2+
Declaration: default checked against union bound when types are concrete
3+
--FILE--
4+
<?php
5+
class Box<T : int | string = float> {}
6+
?>
7+
--EXPECTF--
8+
Fatal error: Default float for type parameter T does not satisfy its bound string|int in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Bound error message: declaration-time default-vs-bound error renders NAMED_WITH_ARGS in the bound
3+
--FILE--
4+
<?php
5+
interface Comparable<T> {}
6+
class Box<T : Comparable<T> = int> {}
7+
?>
8+
--EXPECTF--
9+
Fatal error: Default int for type parameter T does not satisfy its bound Comparable<T> in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Bound check: forwarded T-ref where child's bound is at least as tight as parent's
3+
--FILE--
4+
<?php
5+
class Animal {}
6+
class Dog extends Animal {}
7+
8+
interface A<T : Animal> {}
9+
interface B<Y : Dog> extends A<Y> {}
10+
interface C<Z : Animal> extends A<Z> {}
11+
12+
class IntBox<T : int> {}
13+
class IntSubBox<X : int> extends IntBox<X> {}
14+
15+
echo "OK\n";
16+
?>
17+
--EXPECT--
18+
OK

0 commit comments

Comments
 (0)