Re: [RFC] [Discussion] Bound-Erased Generic Types

From: Date: Mon, 18 May 2026 08:33:45 +0000
Subject: Re: [RFC] [Discussion] Bound-Erased Generic Types
References: 1 2 3 4 5 6 7 8  Groups: php.internals 
Request: Send a blank email to internals+get-130932@lists.php.net to get a copy of this message
Hi Rowan, > In other cases, it sounds like the correct answer is actually an established fact of type theory - e.g. the behaviour of covariance and diamond inheritance. Sharper than it looks. The substitution conflict has existed in PHP all along, just in an indirect form: ```php interface A1 extends Box<A> {} // via @extends docblock interface B1 extends Box<B> {} // via @extends docblock class C implements A1, B1 {} // legal PHP, conflicting substituted prototypes ``` Structurally identical to class C implements Box<A>, Box<B> from the substitution view. SA tools should have caught this years ago, but they didn't. I discovered the bug in Mago while writing the RFC. The direct form is a compile error today (same-interface-twice), so tools never had a forcing function to consider the substituted-conflict case, and the indirect form slipped through. Native generics enable the direct form possible, which forces the diamond-merge check into the engine, and that machinery consequently covers the indirect form as a side effect. > Behind the jargon, [...] All of the rest is just "a syntactic tier that PHP parses but ignores". Two pieces missing from that framing: Reflection metadata. The pre-erasure form is exposed as introspectable data. Frameworks, DI containers, ORMs, serializers can read generic information directly from the engine rather than reparsing source. It's first-class structured data that the language exposes. The bound-as-runtime-type substitution. `class UserCollection extends Collection<User>` substitutes T = User into every T-typed parameter and return; those positions become User-typed at runtime, enforced by the engine. That's real type-checking work. > The biggest advantage I see is the speed of innovation [...] The ~~Type proposal raises two questions I don't see clean answers to: 1. *Lexical grammar inside ~~?* Whitespace breaks conditional types ($var is Foo ? A : B). $ breaks variable-using expressions. { breaks array shapes. < breaks generics. Every plausible stopping rule breaks a class of type expressions SA tools currently support. 2. *AST shape?* Either an opaque string (tools still parse it themselves, still disagree, gain over docblocks is just location) or structured (PHP has committed to an AST for type expressions it doesn't enforce, which is the architectural pattern you wanted to avoid). The speed advantage of docblocks comes from PHP not parsing them at all. Moving syntax into the language reduces that velocity regardless of whether PHP enforces semantics. The RFC's trade-off is to move the *enforceable* parts into the standard and leave the *innovation* parts in docblocks. Future RFCs will incrementally move what's stable enough to standardize, the docblock channel stays open for fast iteration. > I was simply pointing out that some things in the "generic type layer" are not marked by angle brackets [...] Fair, I was answering a different question. At use sites a parameter `TNode $from doesn't carry an indicator that TNode` is a type parameter. That's true of every generic-supporting language; conventions (uppercase-first, T-prefix) emerge to signal it, and the PHP ecosystem already follows them through @template. But you're right that the syntactic marker is at the declaration, not at every use. Cheers, Seifeddine.

« previous php.internals (#130932) next »