mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Zend: Resolve self and parent types at compile time (#17755)
This does not apply to traits.
This commit is contained in:
committed by
GitHub
parent
15d7b83ad3
commit
1ad7743133
@@ -12,7 +12,7 @@ class Foo2 {
|
||||
}
|
||||
|
||||
class Foo3 {
|
||||
public static function __set_state(array $data): Foo3|self {}
|
||||
public static function __set_state(array $data): Foo3|Foo2 {}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
parent type can take part in an intersection type is resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
|
||||
class B extends A {
|
||||
public function foo(): parent&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECT--
|
||||
DONE
|
||||
@@ -1,14 +1,13 @@
|
||||
--TEST--
|
||||
parent type cannot take part in an intersection type
|
||||
parent type cannot take part in an intersection type if not resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
|
||||
class B extends A {
|
||||
trait T {
|
||||
public function foo(): parent&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
parent type cannot take part in an intersection type if not resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
public function foo(): PARENT&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Type PARENT cannot be part of an intersection type in %s on line %d
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
self type can take part in an intersection type is resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public function foo(): self&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECT--
|
||||
DONE
|
||||
@@ -1,12 +1,13 @@
|
||||
--TEST--
|
||||
self type cannot take part in an intersection type
|
||||
self type cannot take part in an intersection type if not resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
trait T {
|
||||
public function foo(): self&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Type self cannot be part of an intersection type in %s on line %d
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
self type cannot take part in an intersection type if not resolvable at compile time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
public function foo(): SELF&Iterator {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Type SELF cannot be part of an intersection type in %s on line %d
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Duplicate parent type in different cases
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data) {}
|
||||
}
|
||||
class Bar extends Foo {
|
||||
public function method(array $data): parent|PARENT {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Duplicate self type in different cases
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data): self|SELF {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Duplicate parent type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data) {}
|
||||
}
|
||||
class Bar extends Foo {
|
||||
public function method(array $data): parent|parent {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Duplicate self type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data): self|self {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Duplicate static type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data): static|static {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type static is redundant in %s on line %d
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Relative class type self resolving to an existing entry (after variation)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data): Foo|self {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Relative class type self resolving to an existing entry (before variation)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data): self|Foo {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Relative class type parent resolving to an existing entry (after variation)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data) {}
|
||||
}
|
||||
class Bar extends Foo {
|
||||
public function method(array $data): Foo|parent {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Relative class type parent resolving to an existing entry (before variation)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public function method(array $data) {}
|
||||
}
|
||||
class Bar extends Foo {
|
||||
public function method(array $data): parent|Foo {}
|
||||
}
|
||||
|
||||
?>
|
||||
DONE
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate type Foo is redundant in %s on line %d
|
||||
@@ -1409,10 +1409,8 @@ static zend_string *add_intersection_type(zend_string *str,
|
||||
ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) {
|
||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type));
|
||||
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type));
|
||||
zend_string *name = ZEND_TYPE_NAME(*single_type);
|
||||
zend_string *resolved = resolve_class_name(name, scope);
|
||||
intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true);
|
||||
zend_string_release(resolved);
|
||||
|
||||
intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true);
|
||||
} ZEND_TYPE_LIST_FOREACH_END();
|
||||
|
||||
ZEND_ASSERT(intersection_str);
|
||||
@@ -1444,6 +1442,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
|
||||
}
|
||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
||||
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
|
||||
|
||||
zend_string *name = ZEND_TYPE_NAME(*list_type);
|
||||
zend_string *resolved = resolve_class_name(name, scope);
|
||||
str = add_type_string(str, resolved, /* is_intersection */ false);
|
||||
@@ -6957,14 +6956,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
|
||||
|
||||
return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0);
|
||||
} else {
|
||||
zend_string *class_name = zend_ast_get_str(ast);
|
||||
uint8_t type_code = zend_lookup_builtin_type_by_name(class_name);
|
||||
zend_string *type_name = zend_ast_get_str(ast);
|
||||
uint8_t type_code = zend_lookup_builtin_type_by_name(type_name);
|
||||
|
||||
if (type_code != 0) {
|
||||
if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Type declaration '%s' must be unqualified",
|
||||
ZSTR_VAL(zend_string_tolower(class_name)));
|
||||
ZSTR_VAL(zend_string_tolower(type_name)));
|
||||
}
|
||||
|
||||
/* Transform iterable into a type union alias */
|
||||
@@ -6978,38 +6977,55 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
|
||||
return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0);
|
||||
} else {
|
||||
const char *correct_name;
|
||||
zend_string *orig_name = zend_ast_get_str(ast);
|
||||
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
|
||||
zend_string *class_name = type_name;
|
||||
|
||||
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
|
||||
class_name = zend_resolve_class_name_ast(ast);
|
||||
zend_assert_valid_class_name(class_name, "a type name");
|
||||
} else {
|
||||
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_SELF || fetch_type == ZEND_FETCH_CLASS_PARENT);
|
||||
|
||||
zend_ensure_valid_class_fetch_type(fetch_type);
|
||||
if (fetch_type == ZEND_FETCH_CLASS_SELF) {
|
||||
/* Scope might be unknown for unbound closures and traits */
|
||||
if (zend_is_scope_known()) {
|
||||
class_name = CG(active_class_entry)->name;
|
||||
ZEND_ASSERT(class_name && "must know class name when resolving self type at compile time");
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT);
|
||||
/* Scope might be unknown for unbound closures and traits */
|
||||
if (zend_is_scope_known()) {
|
||||
class_name = CG(active_class_entry)->parent_name;
|
||||
ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time");
|
||||
}
|
||||
}
|
||||
zend_string_addref(class_name);
|
||||
}
|
||||
|
||||
if (ast->attr == ZEND_NAME_NOT_FQ
|
||||
&& zend_is_confusable_type(orig_name, &correct_name)
|
||||
&& zend_is_not_imported(orig_name)) {
|
||||
&& zend_is_confusable_type(type_name, &correct_name)
|
||||
&& zend_is_not_imported(type_name)) {
|
||||
const char *extra =
|
||||
FC(current_namespace) ? " or import the class with \"use\"" : "";
|
||||
if (correct_name) {
|
||||
zend_error(E_COMPILE_WARNING,
|
||||
"\"%s\" will be interpreted as a class name. Did you mean \"%s\"? "
|
||||
"Write \"\\%s\"%s to suppress this warning",
|
||||
ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra);
|
||||
ZSTR_VAL(type_name), correct_name, ZSTR_VAL(class_name), extra);
|
||||
} else {
|
||||
zend_error(E_COMPILE_WARNING,
|
||||
"\"%s\" is not a supported builtin type "
|
||||
"and will be interpreted as a class name. "
|
||||
"Write \"\\%s\"%s to suppress this warning",
|
||||
ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra);
|
||||
ZSTR_VAL(type_name), ZSTR_VAL(class_name), extra);
|
||||
}
|
||||
}
|
||||
|
||||
class_name = zend_new_interned_string(class_name);
|
||||
zend_alloc_ce_cache(class_name);
|
||||
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
|
||||
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7132,6 +7148,7 @@ static zend_type zend_compile_typename_ex(
|
||||
/* Switch from single name to name list. */
|
||||
type_list->num_types = 1;
|
||||
type_list->types[0] = type;
|
||||
/* Clear MAY_BE_* type flags */
|
||||
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
|
||||
}
|
||||
/* Mark type as list type */
|
||||
@@ -7178,6 +7195,7 @@ static zend_type zend_compile_typename_ex(
|
||||
"Type contains both true and false, bool must be used instead");
|
||||
}
|
||||
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
|
||||
/* Clear MAY_BE_* type flags */
|
||||
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
|
||||
|
||||
if (ZEND_TYPE_IS_COMPLEX(single_type)) {
|
||||
@@ -7190,6 +7208,7 @@ static zend_type zend_compile_typename_ex(
|
||||
/* Switch from single name to name list. */
|
||||
type_list->num_types = 1;
|
||||
type_list->types[0] = type;
|
||||
/* Clear MAY_BE_* type flags */
|
||||
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
|
||||
ZEND_TYPE_SET_LIST(type, type_list);
|
||||
}
|
||||
@@ -7253,8 +7272,10 @@ static zend_type zend_compile_typename_ex(
|
||||
zend_string_release_ex(standard_type_str, false);
|
||||
}
|
||||
/* Check for "self" and "parent" too */
|
||||
if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self")
|
||||
|| zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) {
|
||||
if (
|
||||
zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_SELF))
|
||||
|| zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_PARENT))
|
||||
) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type)));
|
||||
}
|
||||
|
||||
@@ -624,6 +624,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
|
||||
_(ZEND_STR_NULL_LOWERCASE, "null") \
|
||||
_(ZEND_STR_MIXED, "mixed") \
|
||||
_(ZEND_STR_TRAVERSABLE, "Traversable") \
|
||||
_(ZEND_STR_SELF, "self") \
|
||||
_(ZEND_STR_PARENT, "parent") \
|
||||
_(ZEND_STR_SLEEP, "__sleep") \
|
||||
_(ZEND_STR_WAKEUP, "__wakeup") \
|
||||
_(ZEND_STR_CASES, "cases") \
|
||||
|
||||
@@ -166,12 +166,12 @@ string(10) "SplSubject"
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
string(4) "self"
|
||||
string(1) "c"
|
||||
** Method 2 - parameter 0
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
string(6) "parent"
|
||||
string(8) "stdClass"
|
||||
** Method 3 - parameter 0
|
||||
bool(true)
|
||||
bool(false)
|
||||
@@ -195,12 +195,12 @@ string(3) "int"
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
string(4) "self"
|
||||
string(1) "c"
|
||||
** Function/method return type 4
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
string(6) "parent"
|
||||
string(8) "stdClass"
|
||||
** Function/method return type 5
|
||||
bool(true)
|
||||
bool(false)
|
||||
|
||||
@@ -48,13 +48,13 @@ foreach ((new ReflectionClass(C::class))->getMethods() as $method) {
|
||||
--EXPECT--
|
||||
C::a()
|
||||
$method->getReturnType() returns ReflectionNamedType
|
||||
$method->getReturnType()->__toString() returns self
|
||||
$method->getReturnType()->__toString() returns C
|
||||
|
||||
C::b()
|
||||
$method->getReturnType() returns ReflectionUnionType
|
||||
$method->getReturnType()->__toString() returns stdClass|self
|
||||
$method->getReturnType()->__toString() returns stdClass|C
|
||||
$method->getReturnType()->getTypes() returns an array with 2 element(s)
|
||||
type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(self)
|
||||
type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(C)
|
||||
|
||||
C::c()
|
||||
$method->getReturnType() returns ReflectionNamedType
|
||||
Reference in New Issue
Block a user