Skip to content

Commit f58e2da

Browse files
Girgiasnikic
authored andcommitted
Support static type in intersection
Probably should not use the arena directly anymore Also variance tests need to be done
1 parent 5fd525a commit f58e2da

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);
@@ -6301,8 +6301,9 @@ static zend_type zend_compile_typename(
63016301
} else if (ast->kind == ZEND_AST_TYPE_INTERSECTION) {
63026302
zend_ast_list *list = zend_ast_get_list(ast);
63036303
zend_type_list *type_list;
6304+
unsigned int has_static = 0;
63046305

6305-
// TODO Is this still true if self/parent are accepted?
6306+
// TODO static means that one slot may be unused
63066307
/* Allocate the type list directly on the arena as it must be a type
63076308
* list of the same number of elements as the AST list has children */
63086309
type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->children));
@@ -6315,13 +6316,27 @@ static zend_type zend_compile_typename(
63156316
zend_type single_type = zend_compile_single_typename(type_ast);
63166317

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

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

@@ -6336,7 +6351,7 @@ static zend_type zend_compile_typename(
63366351
}
63376352
}
63386353

6339-
ZEND_ASSERT(list->children == type_list->num_types);
6354+
ZEND_ASSERT((list->children - has_static) == type_list->num_types);
63406355

63416356
ZEND_TYPE_SET_LIST(type, type_list);
63426357
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
@@ -847,6 +847,7 @@ static bool zend_check_and_resolve_property_class_type(
847847
zend_type *list_type;
848848

849849
if (ZEND_TYPE_IS_INTERSECTION(info->type)) {
850+
/* Property class can't have static as a type so need to check it here */
850851
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
851852
if (ZEND_TYPE_HAS_NAME(*list_type)) {
852853
zend_string *name = ZEND_TYPE_NAME(*list_type);
@@ -1021,6 +1022,10 @@ static zend_always_inline bool zend_check_type_slow(
10211022
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
10221023
zend_type *list_type;
10231024
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
1025+
/* Check if intersection has static inside it */
1026+
if ((ZEND_TYPE_FULL_MASK(*type) & MAY_BE_STATIC) && !zend_value_instanceof_static(arg)) {
1027+
return false;
1028+
}
10241029
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
10251030
if (HAVE_CACHE_SLOT && *cache_slot) {
10261031
ce = *cache_slot;

0 commit comments

Comments
 (0)