2.3.x-dev: internal error "The initial type of an unresolved template argument is never itself unresolved" (unresolvedTemplateArguments)
Bug report
[!NOTE] This only happens on unreleased
2.3.x-dev(7c94037). Every released version is fine. I ran the2.3.xbranch on our codebase early out of curiosity about phpstan/phpstan-src#6604, phpstan/phpstan-src#6610, phpstan/phpstan-src#6617 and phpstan/phpstan-src#6622, so this isn't blocking anything; I'm reporting it early in case it helps before 2.3.0.
With featureToggles.unresolvedTemplateArguments enabled (part of bleeding edge on 2.3.x), the following crashes PHPStan with an internal error, even at level 0:
Internal error: The initial type of an unresolved template argument is never itself unresolved./**
* @template-covariant T
*/
final class Box
{
/**
* @template U
* @param U $value
* @return self<U>
*/
public static function of(mixed $value): self
{
return new self();
}
/**
* @template U
* @param self<U> $a
* @param self<U> $b
* @return self<T|U>
*/
public function zip(self $a, self $b): self
{
return new self();
}
public function get(): void
{
}
}
function test(): void
{
Box::of(1)->zip(Box::of(2), Box::of(3))->get(); //
}It takes all three of these; removing any one of them makes it pass:
- the method template
Uinferred from two arguments (the same literal twice crashes too, a single argument doesn't); - a return type mixing it with the class template:
self<T|U>crashes,self<U>doesn't; - an impure method called on the result:
SimpleImpurePoint::createFromVariant()asks for the class display name, which describes theT|Uunion and callsgeneralize()on the unresolved argument.
Turning off unresolvedTemplateArguments makes the error go away.
PHPStan\ShouldNotHappenException: The initial type of an unresolved template argument is never itself unresolved.
in src/Type/Generic/UnresolvedTemplateArgumentType.php:64
#0 src/Type/Generic/UnresolvedTemplateArgumentType.php(93): PHPStan\Type\Generic\UnresolvedTemplateArgumentType->__construct()
#1 src/Type/Generic/UnresolvedTemplateArgumentType.php(224): PHPStan\Type\Generic\UnresolvedTemplateArgumentType->withInitialType()
#2 src/Type/UnionType.php(462): PHPStan\Type\Generic\UnresolvedTemplateArgumentType->generalize()
#3 [internal function]: PHPStan\Type\UnionType::{closure:{closure:PHPStan\Type\UnionType::describe():459}:460}()
#4 src/Type/UnionType.php(460): array_map()
#5 src/Type/VerbosityLevel.php(218): PHPStan\Type\UnionType->{closure:PHPStan\Type\UnionType::describe():459}()
#6 src/Type/UnionType.php(459): PHPStan\Type\VerbosityLevel->handle()
#7 src/Type/Generic/TypeProjectionHelper.php(15): PHPStan\Type\UnionType->describe()
#8 src/Reflection/ClassReflection.php(330): PHPStan\Type\Generic\TypeProjectionHelper::describe()
#9 src/Reflection/Callables/SimpleImpurePoint.php(108): PHPStan\Reflection\ClassReflection->getDisplayName()
#10 src/Analyser/ExprHandler/MethodCallHandler.php(194): PHPStan\Reflection\Callables\SimpleImpurePoint::createFromVariant()
#11 src/Analyser/NodeScopeResolver.php(638): PHPStan\Analyser\ExprHandler\MethodCallHandler->processExpr()
#12 src/Analyser/NodeScopeResolver.php(591): PHPStan\Analyser\NodeScopeResolver->processExprNodeInternal()
#13 src/Analyser/StmtHandler/ExpressionHandler.php(63): PHPStan\Analyser\NodeScopeResolver->processExprNode()
#14 src/Analyser/NodeScopeResolver.php(333): PHPStan\Analyser\StmtHandler\ExpressionHandler->processStmt()
#15 src/Analyser/StatementsHandler.php(308): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
#16 src/Analyser/StatementsHandler.php(398): PHPStan\Analyser\StatementsHandler->processStatementStep()In our codebase it came from a generic Timeline<T> with zip(self<U> ...$others): self<list<T|U|null>>, followed by ->values().
Code snippet that reproduces the problem
The playground runs released versions, so here is a standalone repository instead: https://github.com/gnutix/phpstan-unresolved-template-argument-crash (composer install, then analyse). Its CI run shows the crash.
Expected output
No internal error: the snippet has nothing to report at level 0.
Did PHPStan help you today? Did it make you happy in any way?
The closure-signature and static-variable inference merged this weekend is impressive. On our codebase (level max), it already makes a handful of @phpstan-ignores and baseline entries obsolete and finds a couple of dead branches. Thanks!
Source: phpstan/phpstan