Hey Seifeddine,

On 10.5.2026 21:02:32, Seifeddine Gmati wrote:
Hello Internals,

I'd like to start the discussion on a new RFC adding bound-erased
generics types to PHP.

Generic type parameters can be declared on classes, interfaces,
traits, functions, methods, closures, and arrow functions, with
bounds, defaults, and variance markers. Type parameters erase to their
bound at runtime; the pre-erasure form is preserved for Reflection and
consumed by static analyzers.

- RFC:https://wiki.php.net/rfc/bound_erased_generic_types
- Implementation:https://github.com/php/php-src/pull/21969

Thanks,
Seifeddine.

I have a bunch of questions and feedback:

The requirement of ordering seems unnecessary to me - why would we not want to be able to write <T: Box<U>, U: Box<T>>. Alternatingly recursive types are not unheard of. Seems like an arbitrary restriction; and for compilation purposes it only requires collecting all parameter names before evaluating them.

Your tests also show restrictions around intersection types, e.g. "Type parameter T with bound mixed cannot be part of an intersection type" for 'class Foo {} function x<T>(): T & Foo {}'. What's the motivation behind it? This looks fairly natural to me: x() promises to return an instance of Foo which also fulfills the bound T. Any child class of Foo which happens to implement T will fulfill that contract.

I would like to plead to skip the arity validation, except for "more parameters than allowed": - This inhibits graceful addition of generics - any library adding them requires callers to immediately update all caller sites.   - It would also make addition of generics to Iterator classes etc. completely uncontroversial. - This would be more in line with PHP's general "no type is effectively the highest possible bound" approach. I.e. "class A extends Box" and "class A extends Box<mixed>" would be equivalent. - This would also allow for future incremental runtime generics: you'd start with <never> and as you call stuff with values, the type becomes broader.


This is the one thing which makes the whole RFC a non-starter for me if required:

Typing is optional in PHP!


Your tests show that this specific example is allowed, which strikes me as odd. Why would we not check the arity here?

class Container {}
function f(Container<int> $x): Container<string> { return $x; }


Diamond checks:

Are these necessarily problematic? if you inherit Box<int> and Box<string>, it simply means that the generic parameter, when placed in a contravariant location will accept int|string, when placed into return or property types it'll evaluate to never.

If you disagree (that's possibly fine), a diamond covariant parameter should be allowed in any case though, i.e. if Box<+T>, then an interface shall be able to implement Box<string>, Box<int>. At least at a glance I don't find such a test - if it already works, nice, then please just add the test!


Is class ABox implements Box<self> allowed, or do we need to write implements Box<ABox>?


I'm also not sold on the turbofish syntax. I hate it in Rust, which I have to write nearly daily. I forget these :: SO often. And then the Linter yells at me and I correct it. I understand that there are language limitations, in particular with the array syntax, but honestly, I'd rather just have the parser shift in favor of the existing syntax - for these rare conflicting cases forcing parenthesis around the generic would be nicer, i.e. `[A<B, B>(C)]` would continue carrying the meaning it has today, and we'd require writing `[(A<B, B>(C))]` for that case.


I'm not quite sure if + and - are the proper choices. I'm more used to C# myself with in and out being more obvious to me. I also admit that I initially assumed "+" to be covariant - the sum of stuff accepted, and "-" contravariant, subtracting what can be returned. But this particular bikesheds color is not too important to me.


Otherwise, it's a pretty solid RFC which should be extensible with runtime generics eventually. (In particular runtime generics on the class inheritance level should be a no-brainer to add with the existing syntax.)


Thanks,
Bob

Reply via email to