diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2021-03-18 17:11:56 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-03-18 17:11:56 +0100 |
commit | dcac654fd56a2cecec0c187ba061ecc82bbc9031 (patch) | |
tree | 57fd146f6a4deb209c0ebae32341066cf0cd7836 | |
parent | 2f73cbb1b10f197674f4adb15f39a1d2d1aa4824 (diff) | |
download | php-git-dcac654fd56a2cecec0c187ba061ecc82bbc9031.tar.gz |
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.
-rw-r--r-- | Zend/Optimizer/zend_inference.c | 13 | ||||
-rw-r--r-- | ext/opcache/tests/opt/verify_return_type.phpt | 44 |
2 files changed, 56 insertions, 1 deletions
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 42f37fa0c9..e5d476d01d 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -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; } diff --git a/ext/opcache/tests/opt/verify_return_type.phpt b/ext/opcache/tests/opt/verify_return_type.phpt new file mode 100644 index 0000000000..c1384c7af8 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type.phpt @@ -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 |