mirror of
https://github.com/php/php-src.git
synced 2026-04-02 05:32:28 +02:00
Merge branch 'PHP-8.2'
This commit is contained in:
157
Zend/tests/type_declarations/dnf_types/gh9516.phpt
Normal file
157
Zend/tests/type_declarations/dnf_types/gh9516.phpt
Normal file
@@ -0,0 +1,157 @@
|
||||
--TEST--
|
||||
GH-9516: (A&B)|D as a param should allow AB or D. Not just A.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface A { }
|
||||
interface B { }
|
||||
interface D { }
|
||||
|
||||
class A_ implements A {}
|
||||
class B_ implements B {}
|
||||
class AB_ implements A, B {}
|
||||
class D_ implements D {}
|
||||
|
||||
class T {
|
||||
public function method1((A&B)|D $arg): void {}
|
||||
public function method2((B&A)|D $arg): void {}
|
||||
public function method3(D|(A&B) $arg): void {}
|
||||
public function method4(D|(B&A) $arg): void {}
|
||||
}
|
||||
|
||||
$t = new T;
|
||||
|
||||
try {
|
||||
$t->method1(new A_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method1(new B_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method1(new AB_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method1(new D_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
// Lets try in reverse?
|
||||
try {
|
||||
$t->method2(new A_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method2(new B_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method2(new AB_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method2(new D_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
/* Single before intersection */
|
||||
try {
|
||||
$t->method3(new A_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method3(new B_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method3(new AB_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method3(new D_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
// Lets try in reverse?
|
||||
try {
|
||||
$t->method4(new A_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method4(new B_);
|
||||
echo 'Fail', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method4(new AB_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
try {
|
||||
$t->method4(new D_);
|
||||
echo 'Pass', \PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
echo $throwable->getMessage(), \PHP_EOL;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
T::method1(): Argument #1 ($arg) must be of type (A&B)|D, A_ given, called in %s on line %d
|
||||
T::method1(): Argument #1 ($arg) must be of type (A&B)|D, B_ given, called in %s on line %d
|
||||
Pass
|
||||
Pass
|
||||
T::method2(): Argument #1 ($arg) must be of type (B&A)|D, A_ given, called in %s on line %d
|
||||
T::method2(): Argument #1 ($arg) must be of type (B&A)|D, B_ given, called in %s on line %d
|
||||
Pass
|
||||
Pass
|
||||
T::method3(): Argument #1 ($arg) must be of type D|(A&B), A_ given, called in %s on line %d
|
||||
T::method3(): Argument #1 ($arg) must be of type D|(A&B), B_ given, called in %s on line %d
|
||||
Pass
|
||||
Pass
|
||||
T::method4(): Argument #1 ($arg) must be of type D|(B&A), A_ given, called in %s on line %d
|
||||
T::method4(): Argument #1 ($arg) must be of type D|(B&A), B_ given, called in %s on line %d
|
||||
Pass
|
||||
Pass
|
||||
@@ -2380,7 +2380,23 @@ static size_t zend_type_get_num_classes(zend_type type) {
|
||||
return 0;
|
||||
}
|
||||
if (ZEND_TYPE_HAS_LIST(type)) {
|
||||
return ZEND_TYPE_LIST(type)->num_types;
|
||||
/* Intersection types cannot have nested list types */
|
||||
if (ZEND_TYPE_IS_INTERSECTION(type)) {
|
||||
return ZEND_TYPE_LIST(type)->num_types;
|
||||
}
|
||||
ZEND_ASSERT(ZEND_TYPE_IS_UNION(type));
|
||||
size_t count = 0;
|
||||
zend_type *list_type;
|
||||
|
||||
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
|
||||
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
|
||||
count += ZEND_TYPE_LIST(*list_type)->num_types;
|
||||
} else {
|
||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
||||
count += 1;
|
||||
}
|
||||
} ZEND_TYPE_LIST_FOREACH_END();
|
||||
return count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1018,6 +1018,8 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) {
|
||||
# define HAVE_CACHE_SLOT 1
|
||||
#endif
|
||||
|
||||
#define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;}
|
||||
|
||||
static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
|
||||
void **cache_slot, zend_type *type)
|
||||
{
|
||||
@@ -1050,19 +1052,25 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
|
||||
}
|
||||
|
||||
static bool zend_check_intersection_type_from_cache_slot(zend_type_list *intersection_type_list,
|
||||
zend_class_entry *arg_ce, void **cache_slot)
|
||||
zend_class_entry *arg_ce, void ***cache_slot_ptr)
|
||||
{
|
||||
void **cache_slot = *cache_slot_ptr;
|
||||
zend_class_entry *ce;
|
||||
zend_type *list_type;
|
||||
bool status = true;
|
||||
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
|
||||
ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
|
||||
/* If type is not an instance of one of the types taking part in the
|
||||
* intersection it cannot be a valid instance of the whole intersection type. */
|
||||
if (!ce || !instanceof_function(arg_ce, ce)) {
|
||||
return false;
|
||||
status = false;
|
||||
}
|
||||
PROGRESS_CACHE_SLOT();
|
||||
} ZEND_TYPE_LIST_FOREACH_END();
|
||||
return true;
|
||||
if (HAVE_CACHE_SLOT) {
|
||||
*cache_slot_ptr = cache_slot;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static zend_always_inline bool zend_check_type_slow(
|
||||
@@ -1075,24 +1083,14 @@ static zend_always_inline bool zend_check_type_slow(
|
||||
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
|
||||
zend_type *list_type;
|
||||
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
|
||||
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
|
||||
ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
|
||||
/* If type is not an instance of one of the types taking part in the
|
||||
* intersection it cannot be a valid instance of the whole intersection type. */
|
||||
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) {
|
||||
return false;
|
||||
}
|
||||
if (HAVE_CACHE_SLOT) {
|
||||
cache_slot++;
|
||||
}
|
||||
} ZEND_TYPE_LIST_FOREACH_END();
|
||||
return true;
|
||||
return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot);
|
||||
} else {
|
||||
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
|
||||
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
|
||||
if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), cache_slot)) {
|
||||
if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) {
|
||||
return true;
|
||||
}
|
||||
/* The cache_slot is progressed in zend_check_intersection_type_from_cache_slot() */
|
||||
} else {
|
||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
||||
ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
|
||||
@@ -1100,10 +1098,7 @@ static zend_always_inline bool zend_check_type_slow(
|
||||
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAVE_CACHE_SLOT) {
|
||||
cache_slot++;
|
||||
PROGRESS_CACHE_SLOT();
|
||||
}
|
||||
} ZEND_TYPE_LIST_FOREACH_END();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user