Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d81067d
Bidirectional type narrowing initial implementation
ondrejmirtes Sep 1, 2026
2edf14d
Add regression tests for issues fixed by unresolved template arguments
ondrejmirtes Sep 1, 2026
168c594
Add regression test for #8031
ondrejmirtes Sep 1, 2026
20c3947
Pin bug-15169-calls to the unresolved template argument semantics
ondrejmirtes Sep 6, 2026
8fd2168
Pin the native flavour of template arguments (currently failing)
ondrejmirtes Sep 6, 2026
5fad23a
Avoid retaining call ASTs in template argument caches
ondrejmirtes Sep 6, 2026
3b1a688
Preserve inference constraints from arrow function arguments
ondrejmirtes Sep 6, 2026
a87e6be
Preserve inference constraints from terminating expression branches
ondrejmirtes Sep 6, 2026
1f5f1e0
Preserve inference constraints when foreach scope does not escape
ondrejmirtes Sep 6, 2026
fba5263
Preserve inference constraints from unreachable for loop bodies
ondrejmirtes Sep 6, 2026
c359956
Resolve dependent template bounds and defaults completely
ondrejmirtes Sep 6, 2026
dfd955c
Preserve unresolved template identity in finite type comparisons
ondrejmirtes Sep 6, 2026
6fd7b53
Keep PHPDoc template inference out of native construction types
ondrejmirtes Sep 6, 2026
90fb324
Ignore diagnostic call markers when deciding statement replay
ondrejmirtes Sep 6, 2026
8906711
Reuse analysed bodies for array_map and immediately invoked closures
ondrejmirtes Sep 7, 2026
95ed4fe
Carry template resolution policy in immutable analysis contexts
ondrejmirtes Sep 7, 2026
186e52f
Keep template argument inference compatible with PHP 7.4
ondrejmirtes Sep 7, 2026
d59d8c4
Respect template bounds when inferring from later arguments
ondrejmirtes Sep 7, 2026
8da57e4
Use template bounds for unconstrained generic call arguments
ondrejmirtes Sep 7, 2026
2c6d582
Preserve template defaults when generic calls add no constraints
ondrejmirtes Sep 7, 2026
952645b
Use generic parameter defaults to constrain empty nested collections
ondrejmirtes Sep 7, 2026
194bd0e
Use template bounds for objects passed to impure mixed parameters
ondrejmirtes Sep 7, 2026
5e5139b
Preserve unknown ArrayObject values when constructed from an object
ondrejmirtes Sep 7, 2026
87a4b79
Preserve inferred arguments when return types omit generic arguments
ondrejmirtes Sep 7, 2026
2416411
Use a complete PHP version requirement in Bug14396Test
ondrejmirtes Sep 7, 2026
b379e3d
Update levels expectations for inferred SplObjectStorage values
ondrejmirtes Sep 7, 2026
a82b7e9
Preserve bounds for unconstrained template arguments
ondrejmirtes Sep 7, 2026
66ec5e6
Solve linked invariant template arguments together
ondrejmirtes Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Avoid retaining call ASTs in template argument caches
Keep the call expression as a weak cache key, alongside the weak inference-context key. Cache identity remains exact without extending either object lifetime.

Validation: the cache lifetime assertion fails with the strong expression key and passes with weak keys.
  • Loading branch information
ondrejmirtes committed Sep 7, 2026
commit 5fad23a62cd600af82f13ae7d9a19ff511cf59cf
10 changes: 7 additions & 3 deletions src/Reflection/ResolvedFunctionVariantWithOriginal.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ final class ResolvedFunctionVariantWithOriginal implements ResolvedFunctionVaria

private ?Type $phpDocReturnType = null;

/** @var array{Expr, WeakReference<TemplateArgumentFrame>, bool, Type}|null */
/**
* Cache keys must not keep the call AST or inference context alive.
*
* @var array{WeakReference<Expr>, WeakReference<TemplateArgumentFrame>, bool, Type}|null
*/
private ?array $returnTypeWithUnresolvedTemplateArguments = null;

/**
Expand Down Expand Up @@ -186,7 +190,7 @@ public function getReturnType(): Type
public function getReturnTypeWithUnresolvedTemplateArguments(Expr $site, TemplateArgumentFrame $frame, bool $allowUnresolved): Type
{
$cached = $this->returnTypeWithUnresolvedTemplateArguments;
if ($cached !== null && $cached[0] === $site && $cached[1]->get() === $frame && $cached[2] === $allowUnresolved) {
if ($cached !== null && $cached[0]->get() === $site && $cached[1]->get() === $frame && $cached[2] === $allowUnresolved) {
return $cached[3];
}

Expand All @@ -201,7 +205,7 @@ public function getReturnTypeWithUnresolvedTemplateArguments(Expr $site, Templat
),
false,
);
$this->returnTypeWithUnresolvedTemplateArguments = [$site, WeakReference::create($frame), $allowUnresolved, $type];
$this->returnTypeWithUnresolvedTemplateArguments = [WeakReference::create($site), WeakReference::create($frame), $allowUnresolved, $type];

return $type;
}
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Analyser/Generics/TemplateArgumentResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\VerbosityLevel;
use WeakReference;

class TemplateArgumentResolverTest extends PHPStanTestCase
{
Expand Down Expand Up @@ -264,6 +265,15 @@ public function testReturnTypeCacheDistinguishesImmutableResolutions(): void
$this->assertSame('PHPStan\Type\Test\A\A<1>', self::describe($variant->getReturnTypeWithUnresolvedTemplateArguments($site, $initial, true)));
$this->assertTrue($unresolved->equals($variant->getReturnTypeWithUnresolvedTemplateArguments($site, $collecting, true)));
$this->assertTrue($collecting->isObserving());

$reference = WeakReference::create($collecting);
unset($collecting);
$this->assertNull($reference->get(), 'The return-type cache must not retain the inference context.');

$variant->getReturnTypeWithUnresolvedTemplateArguments($site, $sent, true);
$siteReference = WeakReference::create($site);
unset($site, $marker, $constraints, $unresolved);
$this->assertNull($siteReference->get(), 'The resolved return-type cache must not retain the call AST.');
}

public function testSiteAttributionByTokenPosition(): void
Expand Down