1
0
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:
George Peter Banyard
2022-09-10 11:41:25 +01:00
3 changed files with 189 additions and 21 deletions

View 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

View File

@@ -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;
}

View File

@@ -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();
}