Skip to content

Commit 35046e6

Browse files
committed
Compile time generics W.I.P.
1 parent 46a1534 commit 35046e6

File tree

54 files changed

+1658
-85
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1658
-85
lines changed
Lines changed: 31 additions & 0 deletions
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)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
--TEST--
2+
Concrete example of using AT
3+
--CREDITS--
4+
Levi Morrison
5+
--FILE--
6+
<?php declare(strict_types=1);
7+
8+
namespace Sequence;
9+
10+
// No null. This is probably going to be painful, but let's try it.
11+
interface Sequence<Item : object|array|string|float|int|bool>
12+
{
13+
function next(): ?Item;
14+
15+
/**
16+
* @param callable(Item, Item): Item $f
17+
* @return ?Item
18+
*/
19+
function reduce(callable $f): ?Item;
20+
}
21+
22+
final class StringTablePair
23+
{
24+
public function __construct(
25+
public readonly string $string,
26+
public readonly int $id,
27+
) {}
28+
}
29+
30+
final class StringTableSequence implements Sequence<StringTablePair>
31+
{
32+
private array $strings;
33+
34+
public function __construct(StringTable $string_table) {
35+
$this->strings = $string_table->to_assoc_array();
36+
}
37+
38+
function next(): ?StringTablePair
39+
{
40+
$key = \array_key_first($this->strings);
41+
if (!isset($key)) {
42+
return null;
43+
}
44+
$value = \array_shift($this->strings);
45+
return new StringTablePair($key, $value);
46+
}
47+
48+
/**
49+
* @param callable(Item, Item): Item $f
50+
* @return ?Item
51+
*/
52+
function reduce(callable $f): ?StringTablePair
53+
{
54+
$reduction = $this->next();
55+
if (!isset($reduction)) {
56+
return null;
57+
}
58+
59+
while (($next = $this->next()) !== null) {
60+
$reduction = $f($reduction, $next);
61+
}
62+
return $reduction;
63+
}
64+
}
65+
66+
final class StringTable
67+
{
68+
private array $strings = ["" => 0];
69+
70+
public function __construct() {}
71+
72+
public function offsetGet(string $offset): int
73+
{
74+
return $this->strings[$offset] ?? throw new \Exception();
75+
}
76+
77+
public function offsetExists(string $offset): bool
78+
{
79+
return \isset($this->strings[$offset]);
80+
}
81+
82+
public function intern(string $str): int
83+
{
84+
return $this->strings[$str]
85+
?? ($this->strings[$str] = \count($this->strings));
86+
}
87+
88+
public function to_sequence(): StringTableSequence
89+
{
90+
return new StringTableSequence($this);
91+
}
92+
93+
public function to_assoc_array(): array {
94+
return $this->strings;
95+
}
96+
}
97+
98+
?>
99+
--EXPECTF--
100+
Fatal error: Generic type cannot be part of a union type in %s on line %d
Lines changed: 31 additions & 0 deletions
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)
Lines changed: 16 additions & 0 deletions
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<float> {
11+
public function foo(float $param): float {}
12+
}
13+
14+
?>
15+
--EXPECTF--
16+
Fatal error: Bound type float is not a subtype of the constraint type string|int of generic type T of interface I 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+
Cannot use generic type as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T1, T2 : T1> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use generic parameter T1 to constrain generic parameter T2 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+
Cannot use never as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : never> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T 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+
Cannot use void as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : static> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T 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+
Cannot use void as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : void> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T in %s on line %d
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Abstract generic type behaviour in extended interface violates type constraint
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: Constraint type mixed of generic type T of interface I2 is not a subtype of the constraint type (Traversable&Countable)|string|int of generic type T of interface I in %s on line %d
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Implicit interface inheritance with different bound types
3+
--FILE--
4+
<?php
5+
6+
interface I1<T> {
7+
public function foo(T $param): T;
8+
}
9+
10+
interface I2<T> extends I1<T> {
11+
public function bar(int $o, T $param): T;
12+
}
13+
14+
class C implements I2<float>, I1<string> {
15+
public function foo(float $param): float {}
16+
public function bar(int $o, float $param): float {}
17+
}
18+
19+
?>
20+
--EXPECTF--
21+
Fatal error: Bound type T for interface I1 implemented explicitly in C with type string must match the implicitly bound type float from interface I2 in %s on line %d

0 commit comments

Comments
 (0)