Skip to content

Add support for tentative return types of internal methods #6971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
4 changes: 2 additions & 2 deletions Zend/Optimizer/zend_func_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ ZEND_API uint32_t zend_get_func_info(
#endif

ret = zend_get_return_info_from_signature_only(
callee_func, /* script */ NULL, ce, ce_is_instanceof);
callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);

#if ZEND_DEBUG
if (internal_ret) {
Expand Down Expand Up @@ -884,7 +884,7 @@ ZEND_API uint32_t zend_get_func_info(
}
if (!ret) {
ret = zend_get_return_info_from_signature_only(
callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof);
callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
}
}
return ret;
Expand Down
24 changes: 13 additions & 11 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -4001,9 +4001,11 @@ static int is_recursive_tail_call(const zend_op_array *op_array,

uint32_t zend_get_return_info_from_signature_only(
const zend_function *func, const zend_script *script,
zend_class_entry **ce, bool *ce_is_instanceof) {
zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info) {
uint32_t type;
if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE &&
(use_tentative_return_info || !ZEND_ARG_TYPE_IS_TENTATIVE(func->common.arg_info - 1))
) {
zend_arg_info *ret_info = func->common.arg_info - 1;
type = zend_fetch_arg_info_type(script, ret_info, ce);
*ce_is_instanceof = ce != NULL;
Expand All @@ -4025,15 +4027,15 @@ uint32_t zend_get_return_info_from_signature_only(
ZEND_API void zend_init_func_return_info(
const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret)
{
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_ssa_range tmp_range = {0, 0, 0, 0};
bool is_instanceof = false;
ret->type = zend_get_return_info_from_signature_only(
(zend_function *) op_array, script, &ret->ce, &is_instanceof);
ret->is_instanceof = is_instanceof;
ret->range = tmp_range;
ret->has_range = 0;
}
ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE));

zend_ssa_range tmp_range = {0, 0, 0, 0};
bool is_instanceof = false;
ret->type = zend_get_return_info_from_signature_only(
(zend_function *) op_array, script, &ret->ce, &is_instanceof, /* use_tentative_return_info */ 1);
ret->is_instanceof = is_instanceof;
ret->range = tmp_range;
ret->has_range = 0;
}

void zend_func_return_info(const zend_op_array *op_array,
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/zend_inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ ZEND_API void zend_init_func_return_info(
const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
uint32_t zend_get_return_info_from_signature_only(
const zend_function *func, const zend_script *script,
zend_class_entry **ce, bool *ce_is_instanceof);
zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info);
void zend_func_return_info(const zend_op_array *op_array,
const zend_script *script,
int recursive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ The default value is a class constant in the parent class method's signature.
<?php
class MyDateTimeZone extends DateTimeZone
{
public static function listIdentifiers()
public static function listIdentifiers(): array
{
}
}
?>
--EXPECTF--
Fatal error: Declaration of MyDateTimeZone::listIdentifiers() must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) in %s on line %d
Fatal error: Declaration of MyDateTimeZone::listIdentifiers(): array must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ The default value is a constant in the parent class method's signature.
<?php
class MyDateTimeZone extends DateTimeZone
{
public function getTransitions()
public function getTransitions(): array|false
{
}
}
?>
--EXPECTF--
Fatal error: Declaration of MyDateTimeZone::getTransitions() must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) in %s on line %d
Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ The default value is false in the parent class method's signature.

interface MyDateTimeInterface extends DateTimeInterface
{
public function diff();
public function diff(): DateInterval;
}
?>
--EXPECTF--
Fatal error: Declaration of MyDateTimeInterface::diff() must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false) in %s on line %d
Fatal error: Declaration of MyDateTimeInterface::diff(): DateInterval must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval in %s on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ The default value is an integer in the parent class method's signature.
<?php
class MyDateTime extends DateTime
{
public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false)
public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime
{
}
}
?>
--EXPECTF--
Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false) must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) in %s on line %d
Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime in %s on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ The default value is null in the parent class method's signature.
<?php
class MyDateTime extends DateTime
{
public static function createFromFormat()
public static function createFromFormat(): DateTime|false
{
}
}
?>
--EXPECTF--
Fatal error: Declaration of MyDateTime::createFromFormat() must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) in %s on line %d
Fatal error: Declaration of MyDateTime::createFromFormat(): DateTime|false must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false in %s on line %d
12 changes: 0 additions & 12 deletions Zend/tests/type_declarations/variance/internal_parent.phpt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Test that no notice is emitted when the return type/value of the overriding method is compatible with the tentative return type/value of the overridden method
--FILE--
<?php
class MyDateTimeZone extends DateTimeZone
{
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array
{
return [];
}
}

var_dump(MyDateTimeZone::listIdentifiers());
?>
--EXPECT--
array(0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Test that a notice is emitted when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method
--FILE--
<?php
class MyDateTimeZone extends DateTimeZone
{
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string
{
return "";
}
}

var_dump(MyDateTimeZone::listIdentifiers());
?>
--EXPECTF--
Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
string(0) ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Test that a notice is emitted when the tentative return type of the overridden method is omitted
--FILE--
<?php
class MyDateTimeZone extends DateTimeZone
{
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null)
{
}
}
?>
--EXPECTF--
Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Test unresolvable inheritance check due to unavailable parameter type when the parent has a tentative return type.
--FILE--
<?php

class Test extends DateTime {
public static function createFromFormat($format, $datetime, Wrong $timezone = null): DateTime|false {}
}

?>
--EXPECTF--
Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null): DateTime|false and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Test unresolvable inheritance check due to unavailable return type when the parent has a tentative return type.
--FILE--
<?php

class Test extends DateTime {
public static function createFromFormat($format, $datetime, $timezone = null): Wrong { }
}

?>
--EXPECTF--
Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, $timezone = null): Wrong and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Test that the ReturnTypeWillChange attribute cannot target classes
--FILE--
<?php

#[ReturnTypeWillChange]
class Foo
{
}

?>
--EXPECTF--
Fatal error: Attribute "ReturnTypeWillChange" cannot target class (allowed targets: method) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test that the ReturnTypeWillChange attribute cannot target functions
--FILE--
<?php

#[ReturnTypeWillChange]
function foo() {}

?>
--EXPECTF--
Fatal error: Attribute "ReturnTypeWillChange" cannot target function (allowed targets: method) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Test that the ReturnTypeWillChange attribute cannot be used with functions
--FILE--
<?php

class Foo
{
#[ReturnTypeWillChange]
public int $bar;
}

?>
--EXPECTF--
Fatal error: Attribute "ReturnTypeWillChange" cannot target property (allowed targets: method) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method
--FILE--
<?php

class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[ReturnTypeWillChange]
public function modify(string $modifier) {
return false;
}
}

$date = new MyDateTime("2021-01-01 00:00:00");
var_dump($date->modify("+1 sec"));
?>
--EXPECT--
bool(false)
Loading