-
Notifications
You must be signed in to change notification settings - Fork 507
Type projections, part 1: call-site variance in GenericObjectType #2471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
You've opened the pull request against the latest branch 1.11.x. If your code is relevant on 1.10.x and you want it to be released sooner, please rebase your pull request and change its target to 1.10.x. |
I don't trust Rector with downgrading switch to match, please rewrite the code manually 😊 I'd love to review this PR but please make the build green first - there are some failures in integration projects but everything in phpstan-src should be green. Thanks! |
e086d29
to
b82cc17
Compare
Ok, rewritten. From what I've skimmed through, all failures in phpstan-src were related to that |
Soo if I remember our conversation correctly, the idea is:
The above points describe "declaration site variance". Type projections are basically "call site variance", meaning:
Can you tell me if this PR allows me to do the second bullet list? And do you plan to send more PRs, and what they are gonna do? Thank you :) |
Sure :)
Correct.
To be more precise, we want to pass /** @param Collection<covariant Animal> $collection */
function doSomething(Collection $collection): void
{
// now this function accepts Collection<Dog>
// at the cost of not being able to call $collection->add()
} This PR allows – and tests – the first part ("now this function accepts
I'm planning to send two more PRs:
I have the code more or less ready, so the PRs should come in quick succession. I just didn't want to overwhelm you like last time 😅 |
b82cc17
to
174fc20
Compare
src/Reflection/ClassReflection.php
Outdated
@@ -1255,6 +1271,32 @@ public function getPossiblyIncompleteActiveTemplateTypeMap(): TemplateTypeMap | |||
return $this->resolvedTemplateTypeMap ?? $this->getTemplateTypeMap(); | |||
} | |||
|
|||
public function getTemplateTypeVarianceMap(): TemplateTypeVarianceMap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get the need of having these on ClassReflection. Declare-site variance is already taken care of as part of TemplateTag and ClassReflection::getTemplateTags()
. Call-site variance is taken care of in GenericObjectType. Why do we need this in ClassReflection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of other code deals with ClassReflection
rather than GenericObjectType
. As of now, I think the main benefit is in the getDisplayName()
so that the type displayed in error messages matches the type that's written in code:
<?php
/** @template T */
interface Collection
{
function clear(int $flags): void;
}
/** @param Collection<*> $collection */
function foo(Collection $collection): void
{
$collection->clear();
}
12 Method Collection<*>::clear() invoked with 0 parameters, 1 required.
Without the code in ClassReflection, it would say Collection<mixed>
(or, e.g., Collection<Animal>
instead of Collection<covariant Animal>
) which I find rather misleading.
I'm not so sure about the other usages. Now that I think of it, I think it's not necessary in ObjectType
, because there should always be invariance anyway. I haven't yet found out if the usage in StaticType
is significant or not.
Anyway, even if the methods could become private for the sake of this PR, I would need public access to them in the subsequent PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I get it, but I'd prefer if the method name said something like "projection" or "call site" because right now it's a bit confusing. You can access declaration variance through TemplateTag::getVariance()
too and getTemplateTypeVarianceMap
on ClassReflection
seems like the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, that's fair :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise good to merge :)
src/Reflection/ClassReflection.php
Outdated
@@ -1255,6 +1271,32 @@ public function getPossiblyIncompleteActiveTemplateTypeMap(): TemplateTypeMap | |||
return $this->resolvedTemplateTypeMap ?? $this->getTemplateTypeMap(); | |||
} | |||
|
|||
public function getTemplateTypeVarianceMap(): TemplateTypeVarianceMap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I get it, but I'd prefer if the method name said something like "projection" or "call site" because right now it's a bit confusing. You can access declaration variance through TemplateTag::getVariance()
too and getTemplateTypeVarianceMap
on ClassReflection
seems like the same thing.
b66a0a9
to
c296ac2
Compare
Decided to put this on 1.10.x :) So that it can be released sooner once finished. Thank you! |
I think I've got most of type projections figured out and prototyped, so here we go!
As you suggested, I'm starting small by introducing call-site variance to
GenericObjectType
(andClassReflection
, transitively), and adding support for bivariance intoTemplateTypeVariance
.Looking forward to your thoughts, and to finally having type projections in PHPStan 🤞