Skip to content

Commit

Permalink
Type projections, part 3: call-site restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
jiripudil committed Sep 18, 2023
1 parent b8d819c commit e76e2bf
Show file tree
Hide file tree
Showing 38 changed files with 510 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
Expand Down Expand Up @@ -2141,6 +2142,7 @@ private function createFirstClassCallable(array $variants): Type
$variant->isVariadic(),
$variant->getTemplateTypeMap(),
$variant->getResolvedTemplateTypeMap(),
$variant instanceof ParametersAcceptorWithPhpDocs ? $variant->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
);
}

Expand Down
21 changes: 19 additions & 2 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
use PHPStan\Reflection\ParameterReflectionWithPhpDocs;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
use PHPStan\Reflection\Php\PhpFunctionFromParserNodeReflection;
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
use PHPStan\Reflection\Php\PhpMethodReflection;
Expand All @@ -140,6 +141,8 @@
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
Expand Down Expand Up @@ -2186,7 +2189,16 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
if ($parametersAcceptor !== null) {
$selfOutType = $methodReflection->getSelfOutType();
if ($selfOutType !== null) {
$scope = $scope->assignExpression($expr->var, TemplateTypeHelper::resolveTemplateTypes($selfOutType, $parametersAcceptor->getResolvedTemplateTypeMap()), $scope->getNativeType($expr->var));
$scope = $scope->assignExpression(
$expr->var,
TemplateTypeHelper::resolveTemplateTypes(
$selfOutType,
$parametersAcceptor->getResolvedTemplateTypeMap(),
$parametersAcceptor instanceof ParametersAcceptorWithPhpDocs ? $parametersAcceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
TemplateTypeVariance::createCovariant(),
),
$scope->getNativeType($expr->var),
);
}
}

Expand Down Expand Up @@ -3554,7 +3566,12 @@ private function processArgs(
continue;
}

$paramOutTypes[$parameter->getName()] = TemplateTypeHelper::resolveTemplateTypes($parameter->getOutType(), $parametersAcceptor->getResolvedTemplateTypeMap());
$paramOutTypes[$parameter->getName()] = TemplateTypeHelper::resolveTemplateTypes(
$parameter->getOutType(),
$parametersAcceptor->getResolvedTemplateTypeMap(),
$parametersAcceptor instanceof ParametersAcceptorWithPhpDocs ? $parametersAcceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
TemplateTypeVariance::createCovariant(),
);
}
}

Expand Down
24 changes: 21 additions & 3 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use PHPStan\Reflection\Assertions;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\ResolvedFunctionVariant;
use PHPStan\ShouldNotHappenException;
Expand All @@ -45,6 +46,8 @@
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
Expand Down Expand Up @@ -514,7 +517,12 @@ public function specifyTypesInCondition(
}
}

$asserts = $functionReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes($type, $parametersAcceptor->getResolvedTemplateTypeMap()));
$asserts = $functionReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes(
$type,
$parametersAcceptor->getResolvedTemplateTypeMap(),
$parametersAcceptor instanceof ParametersAcceptorWithPhpDocs ? $parametersAcceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
TemplateTypeVariance::createInvariant(),
));
$specifiedTypes = $this->specifyTypesFromAsserts($context, $expr, $asserts, $parametersAcceptor, $scope);
if ($specifiedTypes !== null) {
return $specifiedTypes;
Expand Down Expand Up @@ -549,7 +557,12 @@ public function specifyTypesInCondition(
}
}

$asserts = $methodReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes($type, $parametersAcceptor->getResolvedTemplateTypeMap()));
$asserts = $methodReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes(
$type,
$parametersAcceptor->getResolvedTemplateTypeMap(),
$parametersAcceptor instanceof ParametersAcceptorWithPhpDocs ? $parametersAcceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
TemplateTypeVariance::createInvariant(),
));
$specifiedTypes = $this->specifyTypesFromAsserts($context, $expr, $asserts, $parametersAcceptor, $scope);
if ($specifiedTypes !== null) {
return $specifiedTypes;
Expand Down Expand Up @@ -589,7 +602,12 @@ public function specifyTypesInCondition(
}
}

$asserts = $staticMethodReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes($type, $parametersAcceptor->getResolvedTemplateTypeMap()));
$asserts = $staticMethodReflection->getAsserts()->mapTypes(static fn (Type $type) => TemplateTypeHelper::resolveTemplateTypes(
$type,
$parametersAcceptor->getResolvedTemplateTypeMap(),
$parametersAcceptor instanceof ParametersAcceptorWithPhpDocs ? $parametersAcceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
TemplateTypeVariance::createInvariant(),
));
$specifiedTypes = $this->specifyTypesFromAsserts($context, $expr, $asserts, $parametersAcceptor, $scope);
if ($specifiedTypes !== null) {
return $specifiedTypes;
Expand Down
16 changes: 12 additions & 4 deletions src/PhpDoc/ResolvedPhpDocBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use PHPStan\Type\ConditionalTypeForParameter;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use function array_key_exists;
Expand Down Expand Up @@ -750,7 +751,7 @@ private static function mergeVarTags(array $varTags, array $parents, array $pare
private static function mergeOneParentVarTags(self $parent, PhpDocBlock $phpDocBlock): ?array
{
foreach ($parent->getVarTags() as $key => $parentVarTag) {
return [$key => self::resolveTemplateTypeInTag($parentVarTag, $phpDocBlock)];
return [$key => self::resolveTemplateTypeInTag($parentVarTag, $phpDocBlock, TemplateTypeVariance::createInvariant())];
}

return null;
Expand Down Expand Up @@ -785,7 +786,7 @@ private static function mergeOneParentParamTags(array $paramTags, self $parent,
continue;
}

$paramTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock);
$paramTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock, TemplateTypeVariance::createContravariant());
}

return $paramTags;
Expand Down Expand Up @@ -834,6 +835,7 @@ private static function mergeOneParentReturnTag(?ReturnTag $returnTag, self $par
$phpDocBlock->transformConditionalReturnTypeWithParameterNameMapping($parentReturnTag->getType()),
)->toImplicit(),
$phpDocBlock,
TemplateTypeVariance::createCovariant(),
);
}

Expand Down Expand Up @@ -959,7 +961,7 @@ private static function mergeOneParentParamOutTags(array $paramOutTags, self $pa
continue;
}

$paramOutTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock);
$paramOutTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock, TemplateTypeVariance::createCovariant());
}

return $paramOutTags;
Expand All @@ -970,11 +972,17 @@ private static function mergeOneParentParamOutTags(array $paramOutTags, self $pa
* @param T $tag
* @return T
*/
private static function resolveTemplateTypeInTag(TypedTag $tag, PhpDocBlock $phpDocBlock): TypedTag
private static function resolveTemplateTypeInTag(
TypedTag $tag,
PhpDocBlock $phpDocBlock,
TemplateTypeVariance $positionVariance,
): TypedTag
{
$type = TemplateTypeHelper::resolveTemplateTypes(
$tag->getType(),
$phpDocBlock->getClassReflection()->getActiveTemplateTypeMap(),
$phpDocBlock->getClassReflection()->getCallSiteVarianceMap(),
$positionVariance,
);
return $tag->withType($type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\MethodsClassReflectionExtension;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeVariance;
use function count;

class AnnotationsMethodsClassReflectionExtension implements MethodsClassReflectionExtension
Expand Down Expand Up @@ -65,6 +66,8 @@ private function findClassReflectionWithMethod(
TemplateTypeHelper::resolveTemplateTypes(
$methodTags[$methodName]->getReturnType(),
$classReflection->getActiveTemplateTypeMap(),
$classReflection->getCallSiteVarianceMap(),
TemplateTypeVariance::createCovariant(),
),
$parameters,
$isStatic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Reflection\PropertiesClassReflectionExtension;
use PHPStan\Reflection\PropertyReflection;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\NeverType;

class AnnotationsPropertiesClassReflectionExtension implements PropertiesClassReflectionExtension
Expand Down Expand Up @@ -55,10 +56,14 @@ private function findClassReflectionWithProperty(
TemplateTypeHelper::resolveTemplateTypes(
$propertyTag->getReadableType() ?? new NeverType(),
$classReflection->getActiveTemplateTypeMap(),
$classReflection->getCallSiteVarianceMap(),
TemplateTypeVariance::createCovariant(),
),
TemplateTypeHelper::resolveTemplateTypes(
$propertyTag->getWritableType() ?? new NeverType(),
$classReflection->getActiveTemplateTypeMap(),
$classReflection->getCallSiteVarianceMap(),
TemplateTypeVariance::createContravariant(),
),
$isReadable,
$isWritable,
Expand Down
6 changes: 6 additions & 0 deletions src/Reflection/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ public function getParentClass(): ?ClassReflection
$extendedType = TemplateTypeHelper::resolveTemplateTypes(
$extendedType,
$this->getPossiblyIncompleteActiveTemplateTypeMap(),
$this->getCallSiteVarianceMap(),
TemplateTypeVariance::createStatic(),
);
}

Expand Down Expand Up @@ -846,6 +848,8 @@ public function getImmediateInterfaces(): array
$implementedType = TemplateTypeHelper::resolveTemplateTypes(
$implementedType,
$this->getPossiblyIncompleteActiveTemplateTypeMap(),
$this->getCallSiteVarianceMap(),
TemplateTypeVariance::createStatic(),
true,
);
}
Expand Down Expand Up @@ -1605,6 +1609,8 @@ public function getResolvedMixinTypes(): array
$types[] = TemplateTypeHelper::resolveTemplateTypes(
$mixinTag->getType(),
$this->getActiveTemplateTypeMap(),
$this->getCallSiteVarianceMap(),
TemplateTypeVariance::createStatic(),
);
}

Expand Down
10 changes: 10 additions & 0 deletions src/Reflection/FunctionVariant.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace PHPStan\Reflection;

use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\Type;

/** @api */
class FunctionVariant implements ParametersAcceptor
{

private TemplateTypeVarianceMap $callSiteVarianceMap;

/**
* @api
* @param array<int, ParameterReflection> $parameters
Expand All @@ -19,8 +22,10 @@ public function __construct(
private array $parameters,
private bool $isVariadic,
private Type $returnType,
?TemplateTypeVarianceMap $callSiteVarianceMap = null,
)
{
$this->callSiteVarianceMap = $callSiteVarianceMap ?? TemplateTypeVarianceMap::createEmpty();
}

public function getTemplateTypeMap(): TemplateTypeMap
Expand All @@ -33,6 +38,11 @@ public function getResolvedTemplateTypeMap(): TemplateTypeMap
return $this->resolvedTemplateTypeMap ?? TemplateTypeMap::createEmpty();
}

public function getCallSiteVarianceMap(): TemplateTypeVarianceMap
{
return $this->callSiteVarianceMap;
}

/**
* @return array<int, ParameterReflection>
*/
Expand Down
3 changes: 3 additions & 0 deletions src/Reflection/FunctionVariantWithPhpDocs.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection;

use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\Type;

/** @api */
Expand All @@ -21,6 +22,7 @@ public function __construct(
Type $returnType,
private Type $phpDocReturnType,
private Type $nativeReturnType,
?TemplateTypeVarianceMap $callSiteVarianceMap = null,
)
{
parent::__construct(
Expand All @@ -29,6 +31,7 @@ public function __construct(
$parameters,
$isVariadic,
$returnType,
$callSiteVarianceMap,
);
}

Expand Down
3 changes: 3 additions & 0 deletions src/Reflection/GenericParametersAcceptorResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Type\ConditionalTypeForParameter;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -101,12 +102,14 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc
$parametersAcceptor->getReturnType(),
$parametersAcceptor->getReturnType(),
new MixedType(),
TemplateTypeVarianceMap::createEmpty(),
);
}

return new ResolvedFunctionVariant(
$parametersAcceptor,
$resolvedTemplateTypeMap,
$parametersAcceptor->getCallSiteVarianceMap(),
$passedArgs,
);
}
Expand Down
6 changes: 6 additions & 0 deletions src/Reflection/InaccessibleMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection;

use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

Expand All @@ -28,6 +29,11 @@ public function getResolvedTemplateTypeMap(): TemplateTypeMap
return TemplateTypeMap::createEmpty();
}

public function getCallSiteVarianceMap(): TemplateTypeVarianceMap
{
return TemplateTypeVarianceMap::createEmpty();
}

/**
* @return array<int, ParameterReflection>
*/
Expand Down

0 comments on commit e76e2bf

Please sign in to comment.