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

From: Date: Wed, 13 May 2026 20:39:41 +0000
Subject: Re: [RFC] [Discussion] Bound-Erased Generic Types
References: 1 2 3 4 5 6 7 8 9 10 11 12 13  Groups: php.internals 
Request: Send a blank email to internals+get-130876@lists.php.net to get a copy of this message
Hi Larry, > Consider, if I read the RFC correctly this will (correctly) error at link time: > > interface Foo<T: DateTimeInterface> {} > class Bar implements Foo<User> {} > > So that part of the type system is enforced. It will therefore be natural for users to expect > that > > new Baz<User>(new Product $p); > > will error on them. I think there's a misreading here that's worth addressing directly. Turbofish on new does check arity and bound. Given: class Baz<T : DateTimeInterface> { public function __construct(public T $date) {} } new Baz::<string>(new DateTimeImmutable()); // TypeError: string does not satisfy DateTimeInterface That's enforced at runtime at the turbofish site. What is *not* enforced is the relationship between the turbofish argument and the constructor parameter. The runtime won't reject `new Baz::<DateTimeImmutable>(new DateTime()) because T` erases to DateTimeInterface for parameter-checking purposes. The user-facing distinction is "turbofish bounds are checked, parametric relationships across multiple uses of T are not.", That's the bound-erasure trade. > But count me out for opt-in enforcement. We either enforce it or we don't. [...] If > call-site enforcement can be made cheap enough to include, it's cheap enough to *always* > include. The premise here is that runtime enforcement can be made cheap enough in the foreseeable future. That's the universal-reification path, and no implementation has yet demonstrated it works under PHP's compilation model. Until we have a solution, "always enforce" isn't a deferrable engineering choice, it's a constraint we currently can't meet. If someone produces a viable design, the path forward is either an opt-in mechanism (using the #[ReifiedGenerics] attribute, the reify keyword, or the declare directive) or a complete switch to reified generics, which would involve a breaking change (BC break). What I won't commit to is "we'll just make it cheap enough later" when the engineers who have tried haven't been able to. > I previously suggested some sort of AOT first-party checker The same constraint applies. Building a first-party static analysis (SA) tool competitive with PHPStan, Psalm, or Mago is a year long project requiring a dedicated team, which we do not have. I covered this in detail earlier in the thread. Even if the engineering were tractable, there's no one currently positioned to do it: I'm not, the Foundation isn't proposing it, and the proposal would be evaluated against the existing tools that have a decade of work behind them. The alternative is what PHP already has: four mature SA tools (Phan, Psalm, PHPStan, Mago, in order of seniority), each maintained, each with significant production use. PHP's documentation could point users to these. a recommendation toward the ecosystem standard rather than an NIH reimplementation. That's a much smaller and more tractable thing to do than building a fifth tool. > Advanced users, who you say are the target audience, will still have to work with both > syntaxes; and will still find differences between tools which aren't covered by the subset of > validation that PHP has taken ownership of. I responded to this in the Rowan reply: PHP's type system has grown one feature per RFC for a decade, and the fact that this RFC doesn't ship class-string<T>, non-empty-string, literal types, or negated types isn't unique to generics; it's how every type-system RFC has worked. Those are each their own RFC, and each can land later. > Is there even a viable future way to include such more-complex checks natively in the future? Yes, and several of them are on the roadmap I'm planning to file once this RFC settles: literal types (extending null/true/false to string/int/float literals), negated types (!"" and similar), tuples, shapes, typed arrays, and a couple of others. The intent is to incrementally move the parts of the PHPDoc type system that PHP can express into PHP itself, so that docblocks return to being documentation rather than the language's type-system fallback. None of those RFCs make sense in isolation; they need this one to land first because they all assume the type-parameter, and generic-instantiation that this RFC introduces. Cheers, Seifeddine.

« previous php.internals (#130876) next »