Skip to content

Deleted PR #13076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ static inline bool can_elide_list_type(
ZEND_ASSERT(!is_intersection);
return can_elide_list_type(script, op_array, use_info, *single_type);
}
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
if (ZEND_TYPE_HAS_PNR(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(*single_type));
zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
zend_string_release(lcname);
bool result = ce && safe_instanceof(use_info->ce, ce);
Expand Down
4 changes: 2 additions & 2 deletions Zend/Optimizer/escape_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
/* These flags will always cause an exception */
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
| ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
if (ce && !ce->num_parents && !ce->create_object && !ce->constructor &&
!ce->destructor && !ce->__get && !ce->__set &&
!(ce->ce_flags & forbidden_flags) &&
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
Expand Down Expand Up @@ -228,7 +228,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
script, op_array, opline);
if (ce && !ce->create_object && !ce->constructor &&
!ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
!ce->destructor && !ce->__get && !ce->__set && !ce->num_parents) {
return 1;
}
break;
Expand Down
47 changes: 17 additions & 30 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -2385,8 +2385,8 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen
if (pce) {
/* As we only have space to store one CE,
* we use a plain object type for class unions. */
if (ZEND_TYPE_HAS_NAME(type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(type));
if (ZEND_TYPE_HAS_PNR(type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(type));
// TODO: Pass through op_array.
*pce = zend_optimizer_get_class_entry(script, NULL, lcname);
zend_string_release_ex(lcname, 0);
Expand Down Expand Up @@ -2470,7 +2470,7 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script *
break;
case ZEND_FETCH_CLASS_PARENT:
if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
ce = op_array->scope->parent;
ce = op_array->scope->parents[0]->ce;
}
break;
}
Expand Down Expand Up @@ -3341,8 +3341,8 @@ static zend_always_inline zend_result _zend_update_type_info(
}
break;
case ZEND_FETCH_CLASS_PARENT:
if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def);
if (op_array->scope && op_array->scope->parents[0]->ce && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
UPDATE_SSA_OBJ_TYPE(op_array->scope->parents[0]->ce, 0, ssa_op->result_def);
} else {
UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
}
Expand Down Expand Up @@ -3992,17 +3992,6 @@ ZEND_API zend_result zend_update_type_info(
return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0);
}

static uint32_t get_class_entry_rank(zend_class_entry *ce) {
uint32_t rank = 0;
if (ce->ce_flags & ZEND_ACC_LINKED) {
while (ce->parent) {
rank++;
ce = ce->parent;
}
}
return rank;
}

/* Compute least common ancestor on class inheritance tree only */
static zend_class_entry *join_class_entries(
zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
Expand All @@ -4014,22 +4003,20 @@ static zend_class_entry *join_class_entries(
return NULL;
}

rank1 = get_class_entry_rank(ce1);
rank2 = get_class_entry_rank(ce2);

while (rank1 != rank2) {
if (rank1 > rank2) {
ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
rank1--;
} else {
ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
rank2--;
}
if (ce1->num_parents > ce2->num_parents) {
rank1 = ce1->num_parents - ce2->num_parents;
rank2 = 0;
} else {
rank1 = 0;
rank2 = ce2->num_parents - ce1->num_parents;
}

ce1 = rank1 > 0 ? ce1->parents[rank1-1]->ce : ce1;
ce2 = rank2 > 0 ? ce2->parents[rank2-1]->ce : ce2;

while (ce1 != ce2) {
ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) || !ce1->num_parents ? NULL : ce1->parents[0]->ce;
ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) || !ce2->num_parents ? NULL : ce2->parents[0]->ce;
}

if (ce1) {
Expand Down Expand Up @@ -5070,7 +5057,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
const zend_class_entry *ce = var_info->ce;

if (var_info->is_instanceof ||
!ce || ce->create_object || ce->__get || ce->__set || ce->parent) {
!ce || ce->create_object || ce->__get || ce->__set || ce->num_parents) {
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug77530.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ echo (2)::class;

?>
--EXPECTF--
Fatal error: Illegal class name in %s on line %d
Fatal error: Cannot use "::class" on int in %s on line %d
20 changes: 20 additions & 0 deletions Zend/tests/generics/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Basic generic class declaration
--FILE--
<?php

interface I<T> {
}

class C<T> {
}

final class F<T> {
}

trait T<P> {
}

?>
--EXPECT--

11 changes: 11 additions & 0 deletions Zend/tests/generics/duplicate_generic_param.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Duplicate generic parameter name
--FILE--
<?php

class Test<T, T> {
}

?>
--EXPECTF--
Fatal error: Duplicate generic parameter T in %s on line %d
73 changes: 73 additions & 0 deletions Zend/tests/generics/generic_param_with_over_types.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
--TEST--
Combining a generic parameter with other types
--FILE--
<?php

abstract class AbstractTest1<T> {
public ?T $prop;
public function method(?T $param) {
var_dump($param);
}
}
class ConcreteTest1 extends AbstractTest1<array> {}

$obj = new ConcreteTest1;
$obj->method([]);
$obj->method(null);
try {
$obj->method("string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

echo "\n";

abstract class AbstractTest2<T> {
public T|int $prop;
public function method(T|int $param) {
var_dump($param);
}
}
class ConcreteTest2 extends AbstractTest2<array> {}

$obj = new ConcreteTest2;
$obj->method([]);
$obj->method(42);
$obj->method("42");

echo "\n";

abstract class AbstractTest3<T> {
public T|stdClass $prop;
public function method(T|stdClass $param) {
var_dump($param);
}
}
class ConcreteTest3 extends AbstractTest3<array> {}

$obj = new ConcreteTest3;
$obj->method([]);
$obj->method(new stdClass);
try {
$obj->method("string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
array(0) {
}
NULL
AbstractTest1::method(): Argument #1 ($param) must be of type ?T (where T = array), string given, called in %s on line %d

array(0) {
}
int(42)
int(42)

array(0) {
}
object(stdClass)#3 (0) {
}
AbstractTest3::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d
66 changes: 66 additions & 0 deletions Zend/tests/generics/inheritance_bind_parent_param.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
--TEST--
Bind direct parent parameter during inheritance
--FILE--
<?php

abstract class WithParam<T> {
public T $prop;

public function method(T $param) {
var_dump($param);
}
}

class ConcreteInt extends WithParam<int> {
}

class ConcreteStdClass extends WithParam<stdClass> {
}

class ConcreteSelf extends WithParam<self> {
}

$obj = new ConcreteInt;
$obj->method(42);
$obj->prop = 42;
var_dump($obj->prop);

try {
$obj->method("string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
$obj->prop = "string";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

$obj = new ConcreteStdClass;
$obj->method(new stdClass);
//$obj->prop = new stdClass;
try {
$obj->method("string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

/* TODO: This broke -- "self" resolves to WithParam here.
$obj = new ConcreteSelf;
$obj->method($obj);
try {
$obj->method(new stdClass);
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
*/

?>
--EXPECTF--
int(42)
int(42)
WithParam::method(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d
Cannot assign string to property WithParam::$prop of type T
object(stdClass)#1 (0) {
}
WithParam::method(): Argument #1 ($param) must be of type T (where T = stdClass), string given, called in %s on line %d
87 changes: 87 additions & 0 deletions Zend/tests/generics/inheritance_bind_parent_param_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
--TEST--
Bind multiple parent parameters during inheritance
--FILE--
<?php

abstract class WithParams<T1, T2> {
public function method(T1 $param1, T2 $param2) {
var_dump($param1);
var_dump($param2);
}
}

abstract class WithSameParam<T> extends WithParams<T, T> {
public function method2(T $param) {
var_dump($param);
}
}

abstract class WithOneFixedParam<T> extends WithParams<int, T> {
public function method2(T $param) {
var_dump($param);
}
}

abstract class WithFirstNullableParam<T> extends WithParams<?T, T> {
}

class ConcreteIntInt extends WithSameParam<int> {
}

class ConcreteIntString extends WithOneFixedParam<string> {
}

class ConcreteNullableIntInt extends WithFirstNullableParam<int> {
}

$obj = new ConcreteIntInt;
$obj->method(42, 42);
$obj->method2(42);

try {
$obj->method("string", "string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
$obj->method2("string");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

echo "\n";
$obj = new ConcreteIntString;
$obj->method(42, "string");
$obj->method2("string");

try {
$obj->method("string", 42);
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
$obj->method2(42);
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

echo "\n";
$obj = new ConcreteNullableIntInt;
$obj->method(null, 42);

?>
--EXPECTF--
int(42)
int(42)
int(42)
WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d
WithSameParam::method2(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d

int(42)
string(6) "string"
string(6) "string"
WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d
string(2) "42"

NULL
int(42)
Loading