Skip to content

Commit c126d48

Browse files
committedFeb 2, 2025··
Generic static type fix
1 parent 4805533 commit c126d48

File tree

2 files changed

+58
-41
lines changed

2 files changed

+58
-41
lines changed
 

‎src/Type/Generic/GenericStaticType.php

+35-41
Original file line numberDiff line numberDiff line change
@@ -96,65 +96,59 @@ public function changeBaseClass(ClassReflection $classReflection): StaticType
9696
return new StaticType($classReflection);
9797
}
9898

99-
// this template type mapping logic is very similar to mapping logic in MutatingScope::exactInstantiation()
100-
// where inferring "new Foo" but with the constructor being only in Foo parent class
99+
$templateTags = $this->getClassReflection()->getTemplateTags();
100+
$i = 0;
101+
$indexedTypes = [];
102+
$indexedVariances = [];
103+
foreach ($templateTags as $typeName => $tag) {
104+
if (!array_key_exists($i, $this->types)) {
105+
break;
106+
}
107+
if (!array_key_exists($i, $this->variances)) {
108+
break;
109+
}
110+
$indexedTypes[$typeName] = $this->types[$i];
111+
$indexedVariances[$typeName] = $this->variances[$i];
112+
$i++;
113+
}
101114

102115
$newType = new GenericObjectType($classReflection->getName(), $classReflection->typeMapToList($classReflection->getTemplateTypeMap()));
103116
$ancestorType = $newType->getAncestorWithClassName($this->getClassName());
104117
if ($ancestorType === null) {
105-
return new self($classReflection, $classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()), $this->subtractedType, $this->variances);
118+
return new self(
119+
$classReflection,
120+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
121+
$this->subtractedType,
122+
$classReflection->varianceMapToList($classReflection->getCallSiteVarianceMap()),
123+
);
106124
}
107125

108-
$ancestorClassReflections = $ancestorType->getObjectClassReflections();
109-
if (count($ancestorClassReflections) !== 1) {
110-
return new self($classReflection, $classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()), $this->subtractedType, $this->variances);
126+
$ancestorClassReflection = $ancestorType->getClassReflection();
127+
if ($ancestorClassReflection === null) {
128+
return new self(
129+
$classReflection,
130+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
131+
$this->subtractedType,
132+
$classReflection->varianceMapToList($classReflection->getCallSiteVarianceMap()),
133+
);
111134
}
112135

113-
$ancestorClassReflection = $ancestorClassReflections[0];
114-
$ancestorMapping = [];
136+
$newClassTypes = [];
137+
$newClassVariances = [];
115138
foreach ($ancestorClassReflection->getActiveTemplateTypeMap()->getTypes() as $typeName => $templateType) {
116139
if (!$templateType instanceof TemplateType) {
117140
continue;
118141
}
119142

120-
$ancestorMapping[$typeName] = $templateType;
121-
}
122-
123-
$resolvedTypeMap = [];
124-
foreach ($ancestorClassReflection->typeMapFromList($this->types)->getTypes() as $typeName => $type) {
125-
if (!array_key_exists($typeName, $ancestorMapping)) {
126-
continue;
127-
}
128-
129-
$ancestorType = $ancestorMapping[$typeName];
130-
if (!$ancestorType->getBound()->isSuperTypeOf($type)->yes()) {
131-
continue;
132-
}
133-
134-
if (!array_key_exists($ancestorType->getName(), $resolvedTypeMap)) {
135-
$resolvedTypeMap[$ancestorType->getName()] = $type;
136-
continue;
137-
}
138-
139-
$resolvedTypeMap[$ancestorType->getName()] = TypeCombinator::union($resolvedTypeMap[$ancestorType->getName()], $type);
140-
}
141-
142-
$resolvedVariances = [];
143-
foreach ($ancestorClassReflection->varianceMapFromList($this->variances)->getVariances() as $typeName => $variance) {
144-
if (!array_key_exists($typeName, $ancestorMapping)) {
145-
continue;
146-
}
147-
148-
$ancestorType = $ancestorMapping[$typeName];
149-
if (!array_key_exists($ancestorType->getName(), $resolvedVariances)) {
150-
$resolvedVariances[$ancestorType->getName()] = $variance;
143+
if (!array_key_exists($typeName, $indexedTypes)) {
151144
continue;
152145
}
153146

154-
$resolvedVariances[$ancestorType->getName()] = $resolvedVariances[$ancestorType->getName()]->compose($variance);
147+
$newClassTypes[$templateType->getName()] = $indexedTypes[$typeName];
148+
$newClassVariances[$templateType->getName()] = $indexedVariances[$typeName];
155149
}
156150

157-
return new self($classReflection, $classReflection->typeMapToList(new TemplateTypeMap($resolvedTypeMap)), $this->subtractedType, $classReflection->varianceMapToList(new TemplateTypeVarianceMap($resolvedVariances)));
151+
return new self($classReflection, $classReflection->typeMapToList(new TemplateTypeMap($newClassTypes)), $this->subtractedType, $classReflection->varianceMapToList(new TemplateTypeVarianceMap($newClassVariances)));
158152
}
159153

160154
public function isSuperTypeOfWithReason(Type $type): IsSuperTypeOfResult

‎tests/PHPStan/Analyser/nsrt/generic-static.php

+23
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ public function test(self $s): void
126126

127127
}
128128

129+
/**
130+
* @template T
131+
* @implements Foo<T, float>
132+
*/
133+
abstract class Inconsistent3 implements Foo
134+
{
135+
136+
public function fluent()
137+
{
138+
139+
}
140+
141+
/**
142+
* @param Inconsistent3<int> $s
143+
*/
144+
public function test(self $s): void
145+
{
146+
assertType('static(GenericStatic\Inconsistent3<T (class GenericStatic\Inconsistent3, argument)>)', $this->fluent());
147+
assertType('GenericStatic\\Inconsistent3<int>', $s->fluent());
148+
}
149+
150+
}
151+
129152
/**
130153
* @template T
131154
* @template K

0 commit comments

Comments
 (0)
Please sign in to comment.