Re: [RFC] [Discussion] Bound-Erased Generic Types
| From: | Rowan Tommins [IMSoP] | Date: | Tue, 09 Jun 2026 14:34:00 +0000 |
| Subject: | Re: [RFC] [Discussion] Bound-Erased Generic Types | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-131188@lists.php.net to get a copy of this message | ||
On 9 June 2026 08:26:12 BST, Brent Roose <brent.roose@jetbrains.com> wrote:
>To reiterate what I've said before: generics aren't a runtime tool.
Generics are as much a runtime tool - or as little - as any other type declaration. If it is useful
to enforce "function foo(int $bar): int" at runtime, it is equally useful to enforce
"function foo<T>(T $bar): T" at runtime.
A mode which erases all runtime checks which can be proven statically would be a valuable addition.
Again, we're into "ship a native analyser" territory, though, because you have to
know which types can be trusted, and which are still unchecked.
Importantly, you can't rely on static analysis outside of a closed system - a library
can't choose to erase all types, and still make assumptions in code based on users not passing
other types.
PHPStan has a specific setting to account for this:
> PHPStan by default doesn’t differentiate between PHPDoc and native types. It considers them
> both as certain.
>
> This might not be what you want in case you’re writing a library whose users might pass a
> wrong argument type to a function. Setting treatPhpDocTypesAsCertain to false relaxes some of the
> rules around type-checking.
Replace "PHPDoc" with "erased" and the statement applies to the current
discussion. For example:
```
interface Foo<T: int|string> {
public function bar(): T;
}
function test(Foo<int> $foo): void {
$bar = $foo->bar();
if ( ! is_int($bar) ) {
throw new \TypeError('Expected an int');
}
}
```
If the generic type can be trusted, then the is_int() check is redundant and can safely be removed.
If generics are checked at runtime, it can be trusted; if the final running system has been
successfully analysed and proven correct, it can be trusted. But analysing this code in isolation
*can't* make it trusted; and analyzing the final running system may find cases which can't
be proven until runtime.
As I understand it, the "partial erasure" approach, where the static analyser decides
which runtime checks are needed, is used by Dart: https://dart.dev/language/type-system
Rowan Tommins
[IMSoP]