Skip to content

Commit 23497a7

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 84b84ec commit 23497a7

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);
@@ -6306,8 +6306,9 @@ static zend_type zend_compile_typename(
63066306
} else if (ast->kind == ZEND_AST_TYPE_INTERSECTION) {
63076307
zend_ast_list *list = zend_ast_get_list(ast);
63086308
zend_type_list *type_list;
6309+
unsigned int has_static = 0;
63096310

6310-
// TODO Is this still true if self/parent are accepted?
6311+
// TODO static means that one slot may be unused
63116312
/* Allocate the type list directly on the arena as it must be a type
63126313
* list of the same number of elements as the AST list has children */
63136314
type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->children));
@@ -6320,13 +6321,27 @@ static zend_type zend_compile_typename(
63206321
zend_type single_type = zend_compile_single_typename(type_ast);
63216322

63226323
/* An intersection of standard types cannot exist so invalidate it */
6323-
if (ZEND_TYPE_IS_ONLY_MASK(single_type)) {
6324+
/* Treat "static" as a class type. */
6325+
if (ZEND_TYPE_IS_ONLY_MASK(single_type) && !(ZEND_TYPE_FULL_MASK(single_type) & MAY_BE_STATIC)) {
63246326
zend_string *standard_type_str = zend_type_to_string(single_type);
63256327
zend_error_noreturn(E_COMPILE_ERROR,
63266328
"Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str));
63276329
zend_string_release_ex(standard_type_str, false);
63286330
}
63296331

6332+
/* If the type is static */
6333+
if (UNEXPECTED(!ZEND_TYPE_IS_COMPLEX(single_type))) {
6334+
/* Check that static doesn't overlap */
6335+
uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & MAY_BE_STATIC;
6336+
if (type_mask_overlap) {
6337+
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type static is redundant");
6338+
}
6339+
ZEND_TYPE_FULL_MASK(type) |= MAY_BE_STATIC;
6340+
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
6341+
has_static = 1;
6342+
continue;
6343+
}
6344+
63306345
/* Add type to the type list */
63316346
type_list->types[type_list->num_types++] = single_type;
63326347

@@ -6341,7 +6356,7 @@ static zend_type zend_compile_typename(
63416356
}
63426357
}
63436358

6344-
ZEND_ASSERT(list->children == type_list->num_types);
6359+
ZEND_ASSERT((list->children - has_static) == type_list->num_types);
63456360

63466361
ZEND_TYPE_SET_LIST(type, type_list);
63476362
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
@@ -856,6 +856,7 @@ static bool zend_check_and_resolve_property_class_type(
856856
zend_type *list_type;
857857

858858
if (ZEND_TYPE_IS_INTERSECTION(info->type)) {
859+
/* Property class can't have static as a type so need to check it here */
859860
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
860861
if (ZEND_TYPE_HAS_NAME(*list_type)) {
861862
zend_string *name = ZEND_TYPE_NAME(*list_type);
@@ -1030,6 +1031,10 @@ static zend_always_inline bool zend_check_type_slow(
10301031
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
10311032
zend_type *list_type;
10321033
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
1034+
/* Check if intersection has static inside it */
1035+
if ((ZEND_TYPE_FULL_MASK(*type) & MAY_BE_STATIC) && !zend_value_instanceof_static(arg)) {
1036+
return false;
1037+
}
10331038
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
10341039
if (HAVE_CACHE_SLOT && *cache_slot) {
10351040
ce = *cache_slot;

0 commit comments

Comments
 (0)