Skip to content

Commit 14b6bb7

Browse files
committed
[skip ci] Get explicit notation working
Have a HT null access somewhere...
1 parent e38380b commit 14b6bb7

23 files changed

+538
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Abstract generic types basic
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T $param): T;
8+
}
9+
10+
class CS implements I<string> {
11+
public function foo(string $param): string {
12+
return $param . '!';
13+
}
14+
}
15+
16+
class CI implements I<int> {
17+
public function foo(int $param): int {
18+
return $param + 42;
19+
}
20+
}
21+
22+
$cs = new CS();
23+
var_dump($cs->foo("Hello"));
24+
25+
$ci = new CI();
26+
var_dump($ci->foo(5));
27+
28+
?>
29+
--EXPECT--
30+
string(6) "Hello!"
31+
int(47)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Abstract generic type with a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : int|string> {
7+
public function foo(T $param): T;
8+
}
9+
10+
class CS implements I<string> {
11+
public function foo(string $param): string {
12+
return $param . '!';
13+
}
14+
}
15+
16+
class CI implements I<int> {
17+
public function foo(int $param): int {
18+
return $param + 42;
19+
}
20+
}
21+
22+
$cs = new CS();
23+
var_dump($cs->foo("Hello"));
24+
25+
$ci = new CI();
26+
var_dump($ci->foo(5));
27+
28+
?>
29+
--EXPECT--
30+
string(6) "Hello!"
31+
int(47)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Abstract generic type with a constraint that is not satisfied
3+
--FILE--
4+
<?php
5+
6+
interface I<T : int|string> {
7+
public function foo(T $param): T;
8+
}
9+
10+
class C implements I {
11+
public function foo(float $param): float {}
12+
}
13+
14+
?>
15+
--EXPECTF--
16+
Fatal error: Declaration of C::foo(float $param): float must be compatible with I::foo(<T : string|int> $param): <T : string|int> in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in intersection (simple intersection with class type)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T&Traversable $param): T&Traversable;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in intersection (DNF type)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(stdClass|(T&Traversable) $param): stdClass|(T&Traversable);
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of an intersection type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in union (simple union with built-in type)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T|int $param): T|int;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of a union type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in union (simple union with class type)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T|stdClass $param): T|stdClass;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of a union type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in union (DNF type)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T|(Traversable&Countable) $param): T|(Traversable&Countable);
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of a union type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in union (nullable type union with ?)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(?T $param): ?T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of a union type in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type cannot be in union (forced allowed null)
3+
--FILE--
4+
<?php
5+
6+
interface I<T> {
7+
public function foo(T $param = null): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Associated type cannot be part of a union type (implicitly nullable due to default null value) in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type in class is invalid
3+
--FILE--
4+
<?php
5+
6+
class C<T> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Parse error: syntax error, unexpected token "<", expecting "{" in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type in trait is invalid
3+
--FILE--
4+
<?php
5+
6+
trait C<T> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Parse error: syntax error, unexpected token "<", expecting "{" in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Abstract generic type that is redeclared
3+
--FILE--
4+
<?php
5+
6+
interface I<T, S, T> {
7+
public function foo(T&Traversable $param): T&Traversable;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate generic parameter T in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Abstract generic type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface I<T : int|string|(Traversable&Countable)> {
7+
public function foo(T $param): T;
8+
}
9+
10+
interface I2<T> extends I<T> {
11+
public function bar(int $o, T $param): T;
12+
}
13+
14+
class C implements I2<string> {
15+
public function foo(string $param): string {}
16+
public function bar(int $o, float $param): float {}
17+
}
18+
19+
?>
20+
--EXPECTF--
21+
Fatal error: Declaration of C::bar(int $o, float $param): float must be compatible with I2::bar(int $o, T $param): T in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Abstract generic type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface I<T : int|string|(Traversable&Countable)> {
7+
public function foo(T $param): T;
8+
}
9+
10+
interface I2<T : int|string|(Traversable&Countable), T2 : float|bool|stdClass> extends I<T> {
11+
public function bar(T2 $o, T $param): T2;
12+
}
13+
14+
class C implements I2<string, float> {
15+
public function foo(string $param): string {}
16+
public function bar(float $o, string $param): float {}
17+
}
18+
19+
?>
20+
DONE
21+
--EXPECT--
22+
DONE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
--TEST--
2+
Multiple abstract generic type
3+
--FILE--
4+
<?php
5+
6+
interface I<K, V> {
7+
public function set(K $key, V $value): void;
8+
public function get(K $key): V;
9+
}
10+
11+
class C1 implements I<int, string> {
12+
public array $a = [];
13+
public function set(int $key, string $value): void {
14+
$this->a[$key] = $value . '!';
15+
}
16+
public function get(int $key): string {
17+
return $this->a[$key];
18+
}
19+
}
20+
21+
class C2 implements I<string, object> {
22+
public array $a = [];
23+
public function set(string $key, object $value): void {
24+
$this->a[$key] = $value;
25+
}
26+
public function get(string $key): object {
27+
return $this->a[$key];
28+
}
29+
}
30+
31+
$c1 = new C1();
32+
$c1->set(5, "Hello");
33+
var_dump($c1->a);
34+
var_dump($c1->get(5));
35+
36+
$c2 = new C2();
37+
$c2->set('C1', $c1);
38+
var_dump($c2->a);
39+
var_dump($c2->get('C1'));
40+
41+
try {
42+
$c1->set('blah', "Hello");
43+
} catch (\Throwable $e) {
44+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
45+
}
46+
47+
48+
?>
49+
--EXPECTF--
50+
array(1) {
51+
[5]=>
52+
string(6) "Hello!"
53+
}
54+
string(6) "Hello!"
55+
array(1) {
56+
["C1"]=>
57+
object(C1)#1 (1) {
58+
["a"]=>
59+
array(1) {
60+
[5]=>
61+
string(6) "Hello!"
62+
}
63+
}
64+
}
65+
object(C1)#1 (1) {
66+
["a"]=>
67+
array(1) {
68+
[5]=>
69+
string(6) "Hello!"
70+
}
71+
}
72+
TypeError: C1::set(): Argument #1 ($key) must be of type int, string given, called in %s on line %d

Zend/zend.h

+3
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ struct _zend_class_entry {
220220

221221
/* Only for interfaces */
222222
HashTable *associated_types;
223+
HashTable *bound_types;
224+
zend_generic_parameter *generic_parameters;
225+
uint32_t num_generic_parameters;
223226

224227
uint32_t enum_backing_type;
225228
HashTable *backed_enum_table;

Zend/zend_ast.h

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ enum _zend_ast_kind {
7070
ZEND_AST_ATTRIBUTE_GROUP,
7171
ZEND_AST_MATCH_ARM_LIST,
7272
ZEND_AST_MODIFIER_LIST,
73+
ZEND_AST_GENERIC_PARAM_LIST,
74+
ZEND_AST_GENERIC_ARG_LIST,
7375

7476
/* 0 child nodes */
7577
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
@@ -155,6 +157,8 @@ enum _zend_ast_kind {
155157
ZEND_AST_NAMED_ARG,
156158
ZEND_AST_PARENT_PROPERTY_HOOK_CALL,
157159
ZEND_AST_ASSOCIATED_TYPE,
160+
ZEND_AST_GENERIC_PARAM,
161+
ZEND_AST_CLASS_REF,
158162

159163
/* 3 child nodes */
160164
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,

0 commit comments

Comments
 (0)