Skip to content

Commit 1ad7743

Browse files
authored
Zend: Resolve self and parent types at compile time (#17755)
This does not apply to traits.
1 parent 15d7b83 commit 1ad7743

20 files changed

+230
-29
lines changed

Zend/tests/magic_methods/magic_methods_021.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Foo2 {
1212
}
1313

1414
class Foo3 {
15-
public static function __set_state(array $data): Foo3|self {}
15+
public static function __set_state(array $data): Foo3|Foo2 {}
1616
}
1717

1818
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
parent type can take part in an intersection type is resolvable at compile time
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
8+
class B extends A {
9+
public function foo(): parent&Iterator {}
10+
}
11+
12+
?>
13+
DONE
14+
--EXPECT--
15+
DONE
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
--TEST--
2-
parent type cannot take part in an intersection type
2+
parent type cannot take part in an intersection type if not resolvable at compile time
33
--FILE--
44
<?php
55

6-
class A {}
7-
8-
class B extends A {
6+
trait T {
97
public function foo(): parent&Iterator {}
108
}
119

1210
?>
11+
DONE
1312
--EXPECTF--
1413
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
parent type cannot take part in an intersection type if not resolvable at compile time
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public function foo(): PARENT&Iterator {}
8+
}
9+
10+
?>
11+
DONE
12+
--EXPECTF--
13+
Fatal error: Type PARENT cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
self type can take part in an intersection type is resolvable at compile time
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function foo(): self&Iterator {}
8+
}
9+
10+
?>
11+
DONE
12+
--EXPECT--
13+
DONE
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
--TEST--
2-
self type cannot take part in an intersection type
2+
self type cannot take part in an intersection type if not resolvable at compile time
33
--FILE--
44
<?php
55

6-
class A {
6+
trait T {
77
public function foo(): self&Iterator {}
88
}
99

1010
?>
11+
DONE
1112
--EXPECTF--
1213
Fatal error: Type self cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
self type cannot take part in an intersection type if not resolvable at compile time
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public function foo(): SELF&Iterator {}
8+
}
9+
10+
?>
11+
DONE
12+
--EXPECTF--
13+
Fatal error: Type SELF cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Duplicate parent type in different cases
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data) {}
8+
}
9+
class Bar extends Foo {
10+
public function method(array $data): parent|PARENT {}
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Duplicate self type in different cases
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data): self|SELF {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Duplicate parent type
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data) {}
8+
}
9+
class Bar extends Foo {
10+
public function method(array $data): parent|parent {}
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Duplicate self type
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data): self|self {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Duplicate static type
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data): static|static {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type static is redundant in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Relative class type self resolving to an existing entry (after variation)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data): Foo|self {}
8+
}
9+
10+
?>
11+
DONE
12+
--EXPECTF--
13+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Relative class type self resolving to an existing entry (before variation)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data): self|Foo {}
8+
}
9+
10+
?>
11+
DONE
12+
--EXPECTF--
13+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Relative class type parent resolving to an existing entry (after variation)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data) {}
8+
}
9+
class Bar extends Foo {
10+
public function method(array $data): Foo|parent {}
11+
}
12+
13+
?>
14+
DONE
15+
--EXPECTF--
16+
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Relative class type parent resolving to an existing entry (before variation)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function method(array $data) {}
8+
}
9+
class Bar extends Foo {
10+
public function method(array $data): parent|Foo {}
11+
}
12+
13+
?>
14+
DONE
15+
--EXPECTF--
16+
Fatal error: Duplicate type Foo is redundant in %s on line %d

Zend/zend_compile.c

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,10 +1409,8 @@ static zend_string *add_intersection_type(zend_string *str,
14091409
ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) {
14101410
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type));
14111411
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type));
1412-
zend_string *name = ZEND_TYPE_NAME(*single_type);
1413-
zend_string *resolved = resolve_class_name(name, scope);
1414-
intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true);
1415-
zend_string_release(resolved);
1412+
1413+
intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true);
14161414
} ZEND_TYPE_LIST_FOREACH_END();
14171415

14181416
ZEND_ASSERT(intersection_str);
@@ -1444,6 +1442,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
14441442
}
14451443
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
14461444
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
1445+
14471446
zend_string *name = ZEND_TYPE_NAME(*list_type);
14481447
zend_string *resolved = resolve_class_name(name, scope);
14491448
str = add_type_string(str, resolved, /* is_intersection */ false);
@@ -6957,14 +6956,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
69576956

69586957
return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0);
69596958
} else {
6960-
zend_string *class_name = zend_ast_get_str(ast);
6961-
uint8_t type_code = zend_lookup_builtin_type_by_name(class_name);
6959+
zend_string *type_name = zend_ast_get_str(ast);
6960+
uint8_t type_code = zend_lookup_builtin_type_by_name(type_name);
69626961

69636962
if (type_code != 0) {
69646963
if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
69656964
zend_error_noreturn(E_COMPILE_ERROR,
69666965
"Type declaration '%s' must be unqualified",
6967-
ZSTR_VAL(zend_string_tolower(class_name)));
6966+
ZSTR_VAL(zend_string_tolower(type_name)));
69686967
}
69696968

69706969
/* Transform iterable into a type union alias */
@@ -6978,38 +6977,55 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
69786977
return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0);
69796978
} else {
69806979
const char *correct_name;
6981-
zend_string *orig_name = zend_ast_get_str(ast);
69826980
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
6981+
zend_string *class_name = type_name;
6982+
69836983
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
69846984
class_name = zend_resolve_class_name_ast(ast);
69856985
zend_assert_valid_class_name(class_name, "a type name");
69866986
} else {
6987+
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_SELF || fetch_type == ZEND_FETCH_CLASS_PARENT);
6988+
69876989
zend_ensure_valid_class_fetch_type(fetch_type);
6990+
if (fetch_type == ZEND_FETCH_CLASS_SELF) {
6991+
/* Scope might be unknown for unbound closures and traits */
6992+
if (zend_is_scope_known()) {
6993+
class_name = CG(active_class_entry)->name;
6994+
ZEND_ASSERT(class_name && "must know class name when resolving self type at compile time");
6995+
}
6996+
} else {
6997+
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT);
6998+
/* Scope might be unknown for unbound closures and traits */
6999+
if (zend_is_scope_known()) {
7000+
class_name = CG(active_class_entry)->parent_name;
7001+
ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time");
7002+
}
7003+
}
69887004
zend_string_addref(class_name);
69897005
}
69907006

69917007
if (ast->attr == ZEND_NAME_NOT_FQ
6992-
&& zend_is_confusable_type(orig_name, &correct_name)
6993-
&& zend_is_not_imported(orig_name)) {
7008+
&& zend_is_confusable_type(type_name, &correct_name)
7009+
&& zend_is_not_imported(type_name)) {
69947010
const char *extra =
69957011
FC(current_namespace) ? " or import the class with \"use\"" : "";
69967012
if (correct_name) {
69977013
zend_error(E_COMPILE_WARNING,
69987014
"\"%s\" will be interpreted as a class name. Did you mean \"%s\"? "
69997015
"Write \"\\%s\"%s to suppress this warning",
7000-
ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra);
7016+
ZSTR_VAL(type_name), correct_name, ZSTR_VAL(class_name), extra);
70017017
} else {
70027018
zend_error(E_COMPILE_WARNING,
70037019
"\"%s\" is not a supported builtin type "
70047020
"and will be interpreted as a class name. "
70057021
"Write \"\\%s\"%s to suppress this warning",
7006-
ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra);
7022+
ZSTR_VAL(type_name), ZSTR_VAL(class_name), extra);
70077023
}
70087024
}
70097025

70107026
class_name = zend_new_interned_string(class_name);
70117027
zend_alloc_ce_cache(class_name);
7012-
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
7028+
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, 0);
70137029
}
70147030
}
70157031
}
@@ -7132,6 +7148,7 @@ static zend_type zend_compile_typename_ex(
71327148
/* Switch from single name to name list. */
71337149
type_list->num_types = 1;
71347150
type_list->types[0] = type;
7151+
/* Clear MAY_BE_* type flags */
71357152
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
71367153
}
71377154
/* Mark type as list type */
@@ -7178,6 +7195,7 @@ static zend_type zend_compile_typename_ex(
71787195
"Type contains both true and false, bool must be used instead");
71797196
}
71807197
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
7198+
/* Clear MAY_BE_* type flags */
71817199
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
71827200

71837201
if (ZEND_TYPE_IS_COMPLEX(single_type)) {
@@ -7190,6 +7208,7 @@ static zend_type zend_compile_typename_ex(
71907208
/* Switch from single name to name list. */
71917209
type_list->num_types = 1;
71927210
type_list->types[0] = type;
7211+
/* Clear MAY_BE_* type flags */
71937212
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
71947213
ZEND_TYPE_SET_LIST(type, type_list);
71957214
}
@@ -7253,8 +7272,10 @@ static zend_type zend_compile_typename_ex(
72537272
zend_string_release_ex(standard_type_str, false);
72547273
}
72557274
/* Check for "self" and "parent" too */
7256-
if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self")
7257-
|| zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) {
7275+
if (
7276+
zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_SELF))
7277+
|| zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_PARENT))
7278+
) {
72587279
zend_error_noreturn(E_COMPILE_ERROR,
72597280
"Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type)));
72607281
}

Zend/zend_string.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
624624
_(ZEND_STR_NULL_LOWERCASE, "null") \
625625
_(ZEND_STR_MIXED, "mixed") \
626626
_(ZEND_STR_TRAVERSABLE, "Traversable") \
627+
_(ZEND_STR_SELF, "self") \
628+
_(ZEND_STR_PARENT, "parent") \
627629
_(ZEND_STR_SLEEP, "__sleep") \
628630
_(ZEND_STR_WAKEUP, "__wakeup") \
629631
_(ZEND_STR_CASES, "cases") \

0 commit comments

Comments
 (0)