1
0
mirror of https://github.com/php/php-src.git synced 2026-04-12 18:43:37 +02:00

Allow inferring narrowed return type

Even if an explicit return type is given, we might still infer
a more narrow one based on return statements. We shouldn't
pessimize this just because a type has been declared.
This commit is contained in:
Nikita Popov
2021-03-18 17:11:56 +01:00
parent 2f73cbb1b1
commit dcac654fd5
2 changed files with 56 additions and 1 deletions

View File

@@ -3519,6 +3519,12 @@ static zend_always_inline int _zend_update_type_info(
} else {
zend_arg_info *ret_info = op_array->arg_info - 1;
tmp = zend_fetch_arg_info_type(script, ret_info, &ce);
// TODO: We could model more precisely how illegal types are converted.
uint32_t extra_types = t1 & ~tmp;
if (!extra_types) {
tmp &= t1;
}
}
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
@@ -4019,6 +4025,11 @@ void zend_func_return_info(const zend_op_array *op_array,
return;
}
if (!ret->type) {
/* We will intersect the type later. */
ret->type = MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY;
}
for (j = 0; j < blocks_count; j++) {
if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) {
zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
@@ -4178,10 +4189,10 @@ void zend_func_return_info(const zend_op_array *op_array,
if (tmp_has_range < 0) {
tmp_has_range = 0;
}
ret->type = tmp;
ret->ce = tmp_ce;
ret->is_instanceof = tmp_is_instanceof;
}
ret->type &= tmp;
ret->range = tmp_range;
ret->has_range = tmp_has_range;
}

View File

@@ -0,0 +1,44 @@
--TEST--
Return type check elision
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.opt_debug_level=0x20000
opcache.preload=
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
class Test1 {
final public function getIntOrFloat(int $i): int|float {
return $i;
}
final public function getInt(): int {
return $this->getIntOrFloat();
}
}
?>
--EXPECTF--
$_main:
; (lines=1, args=0, vars=0, tmps=0)
; (after optimizer)
; %s
0000 RETURN int(1)
Test1::getIntOrFloat:
; (lines=2, args=1, vars=1, tmps=0)
; (after optimizer)
; %s
0000 CV0($i) = RECV 1
0001 RETURN CV0($i)
Test1::getInt:
; (lines=3, args=0, vars=0, tmps=1)
; (after optimizer)
; %s
0000 INIT_METHOD_CALL 0 THIS string("getIntOrFloat")
0001 V0 = DO_UCALL
0002 RETURN V0