Skip to content

Commit cff68c9

Browse files
committed
Support static type in intersection
Probably should not use the arena directly anymore Also variance tests need to be done
1 parent 93ca487 commit cff68c9

File tree

5 files changed

+78
-16
lines changed

5 files changed

+78
-16
lines changed

Zend/tests/type_declarations/intersection_types/invalid_types/invalid_static_type.phpt

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Duplicate static type
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function test(): static&A&static {
8+
}
9+
}
10+
11+
?>
12+
--EXPECTF--
13+
Fatal error: Duplicate type static is redundant in %s on line %d
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Using self/parent/static part of an intersection
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z {}
9+
10+
abstract class A {
11+
abstract public function test(): static&X;
12+
}
13+
class B extends A implements X {
14+
public function test(): static&X {
15+
return new self();
16+
}
17+
}
18+
19+
class C extends B {
20+
public function test(): static&X {
21+
return new $this;
22+
}
23+
}
24+
class CError extends B {}
25+
26+
$c = new C();
27+
$ce = new CError();
28+
29+
// Self tests
30+
var_dump($c->test());
31+
try {
32+
var_dump($ce->test());
33+
} catch (\TypeError $e) {
34+
echo $e->getMessage(), PHP_EOL;
35+
}
36+
37+
?>
38+
--EXPECT--
39+
object(C)#3 (0) {
40+
}
41+
B::test(): Return value must be of type X&CError, B returned

Zend/zend_compile.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
12471247
name = called_scope->name;
12481248
}
12491249
}
1250-
str = add_type_string(str, name, /* is_intersection */ false);
1250+
str = add_type_string(str, name, ZEND_TYPE_IS_INTERSECTION(type));
12511251
}
12521252
if (type_mask & MAY_BE_CALLABLE) {
12531253
str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_CALLABLE), /* is_intersection */ false);
@@ -6299,8 +6299,9 @@ static zend_type zend_compile_typename(
62996299
} else if (ast->kind == ZEND_AST_TYPE_INTERSECTION) {
63006300
zend_ast_list *list = zend_ast_get_list(ast);
63016301
zend_type_list *type_list;
6302+
unsigned int has_static = 0;
63026303

6303-
// TODO Is this still true if self/parent are accepted?
6304+
// TODO static means that one slot may be unused
63046305
/* Allocate the type list directly on the arena as it must be a type
63056306
* list of the same number of elements as the AST list has children */
63066307
type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->children));
@@ -6313,13 +6314,27 @@ static zend_type zend_compile_typename(
63136314
zend_type single_type = zend_compile_single_typename(type_ast);
63146315

63156316
/* An intersection of standard types cannot exist so invalidate it */
6316-
if (ZEND_TYPE_IS_ONLY_MASK(single_type)) {
6317+
/* Treat "static" as a class type. */
6318+
if (ZEND_TYPE_IS_ONLY_MASK(single_type) && !(ZEND_TYPE_FULL_MASK(single_type) & MAY_BE_STATIC)) {
63176319
zend_string *standard_type_str = zend_type_to_string(single_type);
63186320
zend_error_noreturn(E_COMPILE_ERROR,
63196321
"Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str));
63206322
zend_string_release_ex(standard_type_str, false);
63216323
}
63226324

6325+
/* If the type is static */
6326+
if (UNEXPECTED(!ZEND_TYPE_IS_COMPLEX(single_type))) {
6327+
/* Check that static doesn't overlap */
6328+
uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & MAY_BE_STATIC;
6329+
if (type_mask_overlap) {
6330+
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type static is redundant");
6331+
}
6332+
ZEND_TYPE_FULL_MASK(type) |= MAY_BE_STATIC;
6333+
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
6334+
has_static = 1;
6335+
continue;
6336+
}
6337+
63236338
/* Add type to the type list */
63246339
type_list->types[type_list->num_types++] = single_type;
63256340

@@ -6334,7 +6349,7 @@ static zend_type zend_compile_typename(
63346349
}
63356350
}
63366351

6337-
ZEND_ASSERT(list->children == type_list->num_types);
6352+
ZEND_ASSERT((list->children - has_static) == type_list->num_types);
63386353

63396354
ZEND_TYPE_SET_LIST(type, type_list);
63406355
ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;

Zend/zend_execute.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ static bool zend_check_and_resolve_property_class_type(
845845
zend_type *list_type;
846846

847847
if (ZEND_TYPE_IS_INTERSECTION(info->type)) {
848+
/* Property class can't have static as a type so need to check it here */
848849
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
849850
if (ZEND_TYPE_HAS_NAME(*list_type)) {
850851
zend_string *name = ZEND_TYPE_NAME(*list_type);
@@ -1019,6 +1020,10 @@ static zend_always_inline bool zend_check_type_slow(
10191020
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
10201021
zend_type *list_type;
10211022
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
1023+
/* Check if intersection has static inside it */
1024+
if ((ZEND_TYPE_FULL_MASK(*type) & MAY_BE_STATIC) && !zend_value_instanceof_static(arg)) {
1025+
return false;
1026+
}
10221027
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
10231028
if (HAVE_CACHE_SLOT && *cache_slot) {
10241029
ce = *cache_slot;

0 commit comments

Comments
 (0)