diff --git a/src/Analyser/ArgumentsHandler.php b/src/Analyser/ArgumentsHandler.php index 12c8c49a665..5b38d47d033 100644 --- a/src/Analyser/ArgumentsHandler.php +++ b/src/Analyser/ArgumentsHandler.php @@ -44,6 +44,7 @@ use PHPStan\Type\FunctionParameterClosureThisExtension; use PHPStan\Type\FunctionParameterClosureTypeExtension; use PHPStan\Type\FunctionParameterOutTypeExtension; +use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Generic\TemplateTypeVariance; @@ -51,6 +52,7 @@ use PHPStan\Type\MethodParameterClosureTypeExtension; use PHPStan\Type\MethodParameterOutTypeExtension; use PHPStan\Type\MixedType; +use PHPStan\Type\NeverType; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\ResourceType; use PHPStan\Type\StaticMethodParameterClosureThisExtension; @@ -59,6 +61,7 @@ use PHPStan\Type\ThisType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeUtils; use function array_keys; use function array_last; @@ -234,7 +237,7 @@ public function processArgs( // resolution (see gatherClosureArgType()). $originalArgForGather = $arg->getAttribute(ArgumentsNormalizer::ORIGINAL_ARG_ATTRIBUTE) ?? $arg; $gatheredArgTypeByIndex[$i] = $typeDrivenAcceptorSelection - ? $this->gatherClosureArgType($parametersAcceptors, $i, $arg->value, $scope) + ? $this->gatherClosureArgType($nodeScopeResolver, $args, $parametersAcceptors, $namedArgumentsVariants, $gatheredArgTypeByIndex, $i, $arg->value, $scope) : $this->closureTypeResolver->getClosureType($scope, $arg->value, true, $storage); $this->addGatheredArgType($gatheredTypes, $gatheredUnpack, $gatheredHasName, $originalArgForGather, $i, $gatheredArgTypeByIndex[$i]); } @@ -957,9 +960,26 @@ private function argConsumesResolvedParameterType(Expr $value): bool * resolved from sibling args. That keeps the inferred return type (the U in * callable(T): U) faithful to the closure's own declaration. * + * A template of the callee that the raw parameter leaks into the closure + * type (the T in callable(T): T, read back from a closure returning its + * parameter) would infer T from T itself, so it is resolved from the other + * arguments gathered so far instead (see resolveCalleeTemplateTypes()). + * + * @param Node\Arg[] $args * @param ParametersAcceptor[] $parametersAcceptors + * @param ParametersAcceptor[]|null $namedArgumentsVariants + * @param array $gatheredArgTypeByIndex */ - private function gatherClosureArgType(array $parametersAcceptors, int $i, Expr $closureExpr, MutatingScope $scope): Type + private function gatherClosureArgType( + NodeScopeResolver $nodeScopeResolver, + array $args, + array $parametersAcceptors, + ?array $namedArgumentsVariants, + array $gatheredArgTypeByIndex, + int $i, + Expr $closureExpr, + MutatingScope $scope, + ): Type { $rawParameter = null; if (count($parametersAcceptors) === 1) { @@ -971,11 +991,76 @@ private function gatherClosureArgType(array $parametersAcceptors, int $i, Expr $ } } - if ($rawParameter !== null) { - $scope = $scope->pushInFunctionCall(null, $rawParameter, false); + if ($rawParameter === null) { + return $this->closureParameterResolver->resolveCallableTypeForScope($closureExpr, $scope); + } + + $closureType = $this->closureParameterResolver->resolveCallableTypeForScope( + $closureExpr, + $scope->pushInFunctionCall(null, $rawParameter, false), + ); + + $calleeTemplateTypeMap = $parametersAcceptors[0]->getTemplateTypeMap(); + if ($calleeTemplateTypeMap->isEmpty() || !self::referencesCalleeTemplateTypes($closureType, $calleeTemplateTypeMap)) { + return $closureType; + } + + $siblingTypes = []; + $siblingUnpack = false; + $siblingHasName = false; + foreach ($args as $j => $siblingArg) { + $siblingOriginalArg = $siblingArg->getAttribute(ArgumentsNormalizer::ORIGINAL_ARG_ATTRIBUTE) ?? $siblingArg; + $this->addGatheredArgType($siblingTypes, $siblingUnpack, $siblingHasName, $siblingOriginalArg, $j, $j !== $i ? ($gatheredArgTypeByIndex[$j] ?? new MixedType()) : new MixedType()); + } + + return self::resolveCalleeTemplateTypes( + $closureType, + $calleeTemplateTypeMap, + $this->selectArgsMetadataAcceptor($nodeScopeResolver, $args, $siblingTypes, $parametersAcceptors, $namedArgumentsVariants, $siblingHasName, $siblingUnpack, $scope)->getResolvedTemplateTypeMap(), + ); + } + + private static function referencesCalleeTemplateTypes(Type $type, TemplateTypeMap $calleeTemplateTypeMap): bool + { + if (!$type->hasTemplateOrLateResolvableType()) { + return false; + } + + foreach ($type->getReferencedTemplateTypes(TemplateTypeVariance::createInvariant()) as $reference) { + if (self::isCalleeTemplateType($reference->getType(), $calleeTemplateTypeMap)) { + return true; + } } - return $this->closureParameterResolver->resolveCallableTypeForScope($closureExpr, $scope); + return false; + } + + /** + * Replaces the callee's own template types with what the other arguments + * resolve them to - their default or bound when they say nothing about them, + * or pass no value of them at all (never, from an empty array). + */ + private static function resolveCalleeTemplateTypes(Type $type, TemplateTypeMap $calleeTemplateTypeMap, TemplateTypeMap $resolvedTemplateTypeMap): Type + { + return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($calleeTemplateTypeMap, $resolvedTemplateTypeMap): Type { + if (!$type instanceof TemplateType || !self::isCalleeTemplateType($type, $calleeTemplateTypeMap)) { + return $traverse($type); + } + + $resolvedType = $resolvedTemplateTypeMap->getType($type->getName()); + if ($resolvedType === null || $resolvedType instanceof ErrorType || $resolvedType instanceof NeverType) { + return $traverse($type->getDefault() ?? $type->getBound()); + } + + return $resolvedType; + }); + } + + private static function isCalleeTemplateType(TemplateType $type, TemplateTypeMap $calleeTemplateTypeMap): bool + { + $calleeTemplateType = $calleeTemplateTypeMap->getType($type->getName()); + + return $calleeTemplateType instanceof TemplateType && $calleeTemplateType->getScope()->equals($type->getScope()); } /** diff --git a/tests/PHPStan/Analyser/nsrt/bug-15654.php b/tests/PHPStan/Analyser/nsrt/bug-15654.php new file mode 100644 index 00000000000..34dedf6bc3e --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-15654.php @@ -0,0 +1,105 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug15654; + +use function PHPStan\Testing\assertType; + +/** + * @template T + * @param list $list + * @param callable(T): T $fn + */ +function apply(array $list, callable $fn): void {} + +apply(['a', 'b'], static function (string $s): string { + assertType('\'a\'|\'b\'', $s); + echo match ($s) { 'a' => 1, 'b' => 2 }; + return $s; +}); + +apply(['a', 'b'], static function (string $s): string { + assertType('\'a\'|\'b\'', $s); + echo match ($s) { 'a' => 1, 'b' => 2, default => throw new \LogicException() }; + return $s; +}); + +apply(['a', 'b'], static function ($s) { + assertType('\'a\'|\'b\'', $s); + return $s; +}); + +apply([], static function (string $s): string { + assertType('string', $s); + return $s; +}); + +/** + * @template T + * @param list $list + * @param callable(T): T $fn + * @return list + */ +function map(array $list, callable $fn): array +{ + return $list; +} + +function (): void { + assertType('list<\'a\'|\'b\'>', map(['a', 'b'], static fn (string $s): string => $s)); + assertType('list', map(['a', 'b'], static fn (string $s): string => $s . 'x')); +}; + +/** + * @template T + * @param callable(T): T $fn + * @return T + */ +function only(callable $fn) +{ + return $fn(null); +} + +function (): void { + assertType('string', only(static fn (string $s): string => $s)); + assertType('mixed', only(static fn ($s) => $s)); +}; + +class Collection +{ + + /** + * @template T + * @param list $list + * @param callable(T): T $fn + * @return list + */ + public function map(array $list, callable $fn): array + { + return $list; + } + + /** + * @template T + * @param list $list + * @param callable(T): T $fn + * @return list + */ + public static function staticMap(array $list, callable $fn): array + { + return $list; + } + +} + +function (Collection $c): void { + assertType('list<\'a\'|\'b\'>', $c->map(['a', 'b'], static function (string $s): string { + assertType('\'a\'|\'b\'', $s); + return $s; + })); + assertType('list<\'a\'|\'b\'>', Collection::staticMap(['a', 'b'], static function (string $s): string { + assertType('\'a\'|\'b\'', $s); + return $s; + })); +}; diff --git a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php index e216d084f33..4e356d09299 100644 --- a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php @@ -502,6 +502,18 @@ public function testBug12241(): void $this->analyse([__DIR__ . '/data/bug-12241.php'], []); } + #[RequiresPhp('>= 8.0.0')] + public function testBug15654(): void + { + $this->analyse([__DIR__ . '/data/bug-15654.php'], [ + [ + 'Match arm comparison between \'b\' and \'b\' is always true.', + 20, + 'Remove remaining cases below this one and this error will disappear too.', + ], + ]); + } + #[RequiresPhp('>= 8.0.0')] public function testBug14412(): void { diff --git a/tests/PHPStan/Rules/Comparison/data/bug-15654.php b/tests/PHPStan/Rules/Comparison/data/bug-15654.php new file mode 100644 index 00000000000..3ebc540b29e --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-15654.php @@ -0,0 +1,22 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug15654Match; + +/** + * @template T + * @param list $list + * @param callable(T): T $fn + */ +function apply(array $list, callable $fn): void {} + +apply(['a', 'b'], static function (string $s): string { + echo match ($s) { 'a' => 1, 'b' => 2 }; + return $s; +}); + +apply(['a', 'b'], static function (string $s): string { + echo match ($s) { 'a' => 1, 'b' => 2, default => throw new \LogicException() }; + return $s; +}); diff --git a/turbo-ext/src/ArgumentsHandler.cpp b/turbo-ext/src/ArgumentsHandler.cpp index 6e1e1651e14..95a1b55c23c 100644 --- a/turbo-ext/src/ArgumentsHandler.cpp +++ b/turbo-ext/src/ArgumentsHandler.cpp @@ -829,6 +829,126 @@ zv::Val argResultsDependencies(HashTable *argResults) /* }}} */ +/* {{{ the callee's own template types in a gathered closure type */ + +/* $type->getName() of a template type (a string); UNDEF = pending exception */ +zv::Val templateTypeName(zval *type) +{ + zv::Val name = pt_type_call(Z_OBJ_P(type), PT_LC("getname"), 0, NULL); + if (UNEXPECTED(name.isUndef())) return zv::Val(); + if (UNEXPECTED(!zv::Ref(name.raw()).isString())) { + zend_type_error("phpstan_turbo: getName() must return string"); + return zv::Val(); + } + return name; +} + +/* Mirrors ArgumentsHandler::isCalleeTemplateType() of a template type; false + * = pending exception */ +[[nodiscard]] bool isCalleeTemplateType(zval *type, zval *calleeTemplateTypeMap, bool &out) +{ + out = false; + zv::Val name = templateTypeName(type); + if (UNEXPECTED(name.isUndef())) return false; + zv::Val calleeTemplateType = pt_template_type_map_get_type(calleeTemplateTypeMap, Z_STR_P(name.raw())); + if (UNEXPECTED(calleeTemplateType.isUndef())) return false; + bool calleeIsTemplate; + if (UNEXPECTED(!pt_type_instanceof(calleeTemplateType.raw(), PT_CLASS_TEMPLATE_TYPE, calleeIsTemplate))) return false; + if (!calleeIsTemplate) return true; + zv::Val calleeScope = pt_type_call(Z_OBJ_P(calleeTemplateType.raw()), PT_LC("getscope"), 0, NULL); + if (UNEXPECTED(calleeScope.isUndef())) return false; + zv::Val scope = pt_type_call(Z_OBJ_P(type), PT_LC("getscope"), 0, NULL); + if (UNEXPECTED(scope.isUndef())) return false; + return pt_template_type_scope_equals(calleeScope.raw(), scope.raw(), out); +} + +/* Mirrors ArgumentsHandler::referencesCalleeTemplateTypes(); false = pending + * exception */ +[[nodiscard]] bool referencesCalleeTemplateTypes(zval *type, zval *calleeTemplateTypeMap, bool &out) +{ + out = false; + zv::Val hasTemplate = pt_type_op(Z_OBJ_P(type), PT_OP_HAS_TEMPLATE_OR_LATE_RESOLVABLE_TYPE, 0, NULL); + if (UNEXPECTED(hasTemplate.isUndef())) return false; + if (!zend_is_true(hasTemplate.raw())) return true; + zval *invariant = pt_template_type_variance_singleton(PT_TEMPLATE_TYPE_VARIANCE_INVARIANT); + if (UNEXPECTED(invariant == NULL)) return false; + zv::Val references = pt_type_op(Z_OBJ_P(type), PT_OP_GET_REFERENCED_TEMPLATE_TYPES, 1, invariant); + if (UNEXPECTED(references.isUndef())) return false; + if (UNEXPECTED(!zv::Ref(references.raw()).isArray())) { + zend_type_error("phpstan_turbo: getReferencedTemplateTypes() must return array"); + return false; + } + for (zv::ArrayEntry entry : zv::ArrRef(references.raw())) { + zv::Ref reference = entry.value().deref(); + if (UNEXPECTED(!reference.isObject())) { + zend_type_error("phpstan_turbo: a template type reference must be an object"); + return false; + } + zv::Val referenceType, positionVariance; + if (UNEXPECTED(!pt_template_type_reference_parts(reference.raw(), referenceType, positionVariance))) return false; + bool isCallee; + if (UNEXPECTED(!isCalleeTemplateType(referenceType.raw(), calleeTemplateTypeMap, isCallee))) return false; + if (isCallee) { + out = true; + return true; + } + } + return true; +} + +/* the traversal callback of ArgumentsHandler::resolveCalleeTemplateTypes() - + * state0: the callee's template type map, state1: the resolved one */ +void resolveCalleeTemplateTypesBody(zval *calleeTemplateTypeMap, zval *resolvedTemplateTypeMap, uint32_t argc, zval *argv, zval *return_value) +{ + if (UNEXPECTED(argc < 2 || Z_TYPE(argv[0]) != IS_OBJECT)) { + zend_type_error("ArgumentsHandler::resolveCalleeTemplateTypes() traversal: expected (Type $type, callable $traverse)"); + return; + } + zval *type = &argv[0]; + bool isCallee; + if (UNEXPECTED(!pt_type_instanceof(type, PT_CLASS_TEMPLATE_TYPE, isCallee))) return; + if (isCallee && UNEXPECTED(!isCalleeTemplateType(type, calleeTemplateTypeMap, isCallee))) return; + if (!isCallee) { + (void) pt_type_traverser_traverse(return_value, &argv[1], type); + return; + } + + zv::Val name = templateTypeName(type); + if (UNEXPECTED(name.isUndef())) return; + zv::Val resolvedType = pt_template_type_map_get_type(resolvedTemplateTypeMap, Z_STR_P(name.raw())); + if (UNEXPECTED(resolvedType.isUndef())) return; + bool fallBack = resolvedType.isNull(); + if (!fallBack) { + bool isError, isNever; + (void) pt_type_instanceof_ce(resolvedType.raw(), pt_ce_error_type, isError); + (void) pt_type_instanceof_ce(resolvedType.raw(), pt_ce_never_type, isNever); + fallBack = isError || isNever; + } + if (fallBack) { + /* $type->getDefault() ?? $type->getBound() */ + zv::Val fallback = pt_type_call(Z_OBJ_P(type), PT_LC("getdefault"), 0, NULL); + if (UNEXPECTED(fallback.isUndef())) return; + if (fallback.isNull()) { + fallback = pt_type_call(Z_OBJ_P(type), PT_LC("getbound"), 0, NULL); + if (UNEXPECTED(fallback.isUndef())) return; + } + (void) pt_type_traverser_traverse(return_value, &argv[1], fallback.raw()); + return; + } + + resolvedType.intoReturnValue(return_value); +} + +/* Mirrors ArgumentsHandler::resolveCalleeTemplateTypes() */ +zv::Val resolveCalleeTemplateTypes(zval *type, zval *calleeTemplateTypeMap, zval *resolvedTemplateTypeMap) +{ + zv::Val callback = pt_type_native_callback(resolveCalleeTemplateTypesBody, calleeTemplateTypeMap, resolvedTemplateTypeMap); + if (UNEXPECTED(callback.isUndef())) return zv::Val(); + return pt_type_traverser_map_of(type, callback.raw()); +} + +/* }}} */ + } // namespace namespace phpstanturbo { @@ -976,42 +1096,6 @@ class ArgumentsHandler return true; } - /* Mirrors gatherClosureArgType() */ - zv::Val gatherClosureArgType(HashTable *parametersAcceptors, zend_ulong i, zval *closureExpr, zval *scope) const - { - zv::Val rawParametersHold; - zval *rawParameter = NULL; - if (zend_hash_num_elements(parametersAcceptors) == 1) { - zval *acceptor = readIndex(parametersAcceptors, 0); - if (UNEXPECTED(acceptor == NULL)) return zv::Val(); - rawParametersHold = acceptorGetParameters(acceptor); - if (UNEXPECTED(rawParametersHold.isUndef())) return zv::Val(); - if (UNEXPECTED(!requireArray(rawParametersHold.raw(), "getParameters()"))) return zv::Val(); - HashTable *rawParameters = Z_ARRVAL_P(rawParametersHold.raw()); - rawParameter = issetIndex(rawParameters, i); - if (rawParameter == NULL && zend_hash_num_elements(rawParameters) > 0) { - bool isVariadic = false; - acceptor = readIndex(parametersAcceptors, 0); - if (UNEXPECTED(acceptor == NULL || !acceptorIsVariadic(acceptor, isVariadic))) return zv::Val(); - if (isVariadic) { - rawParameter = arrayLast(rawParameters); - ZVAL_DEREF(rawParameter); - } - } - } - - zv::Val pushedScope; - if (rawParameter != NULL && Z_TYPE_P(rawParameter) != IS_NULL) { - zval null; - ZVAL_NULL(&null); - pushedScope = pt_mutating_scope_push_in_function_call(Z_OBJ_P(scope), &null, rawParameter, false); - if (UNEXPECTED(pushedScope.isUndef())) return zv::Val(); - scope = pushedScope.raw(); - } - - return closureParameterResolverResolveCallableTypeForScope(slot(slots::closureParameterResolver), closureExpr, scope); - } - /* Mirrors selectArgsAcceptor() */ static zv::Val selectArgsAcceptor(zval *types, zval *parametersAcceptors, zval *namedArgumentsVariants, bool hasName, bool unpack) { @@ -1733,8 +1817,9 @@ class ArgumentsHandler } /* the metadata acceptor selected over every argument's gathered type - * (allMixed: over mixed for all of them), padded with mixed */ - zend_never_inline zv::Val paddedMetadataAcceptor(Walk &w, bool allMixed) const + * (allMixed: over mixed for all of them; mixedKey: over mixed for that + * argument), padded with mixed */ + zend_never_inline zv::Val paddedMetadataAcceptor(Walk &w, bool allMixed, const zend_ulong *mixedKey = NULL) const { zv::Arr paddedTypes = zv::Arr::empty(); bool paddedUnpack = false; @@ -1744,7 +1829,7 @@ class ArgumentsHandler zval *paddedOriginalArg = originalArgOf(record.arg); zv::Val mixed; zval *type; - if (!allMixed && Z_TYPE(record.gathered) != IS_UNDEF && Z_TYPE(record.gathered) != IS_NULL) { + if (!allMixed && (mixedKey == NULL || record.key != *mixedKey) && Z_TYPE(record.gathered) != IS_UNDEF && Z_TYPE(record.gathered) != IS_NULL) { type = &record.gathered; } else { mixed = pt_type_new_mixed_type(); @@ -2428,6 +2513,64 @@ class ArgumentsHandler return completeOtherArg(w, record, value, a, a.exprResult.raw(), a.enterExpressionAssignForByRef); } + /* Mirrors gatherClosureArgType() */ + zv::Val gatherClosureArgType(Walk &w, zend_ulong i, zval *closureExpr) const + { + HashTable *parametersAcceptors = Z_ARRVAL_P(w.parametersAcceptors); + zval *scope = w.scope.raw(); + zv::Val rawParametersHold; + zval *rawParameter = NULL; + zval *acceptor = NULL; + if (zend_hash_num_elements(parametersAcceptors) == 1) { + acceptor = readIndex(parametersAcceptors, 0); + if (UNEXPECTED(acceptor == NULL)) return zv::Val(); + rawParametersHold = acceptorGetParameters(acceptor); + if (UNEXPECTED(rawParametersHold.isUndef())) return zv::Val(); + if (UNEXPECTED(!requireArray(rawParametersHold.raw(), "getParameters()"))) return zv::Val(); + HashTable *rawParameters = Z_ARRVAL_P(rawParametersHold.raw()); + rawParameter = issetIndex(rawParameters, i); + if (rawParameter == NULL && zend_hash_num_elements(rawParameters) > 0) { + bool isVariadic = false; + if (UNEXPECTED(!acceptorIsVariadic(acceptor, isVariadic))) return zv::Val(); + if (isVariadic) { + rawParameter = arrayLast(rawParameters); + ZVAL_DEREF(rawParameter); + } + } + } + + if (rawParameter == NULL || Z_TYPE_P(rawParameter) == IS_NULL) { + return closureParameterResolverResolveCallableTypeForScope(slot(slots::closureParameterResolver), closureExpr, scope); + } + + zval null; + ZVAL_NULL(&null); + zv::Val pushedScope = pt_mutating_scope_push_in_function_call(Z_OBJ_P(scope), &null, rawParameter, false); + if (UNEXPECTED(pushedScope.isUndef())) return zv::Val(); + zv::Val closureType = closureParameterResolverResolveCallableTypeForScope(slot(slots::closureParameterResolver), closureExpr, pushedScope.raw()); + if (UNEXPECTED(closureType.isUndef())) return zv::Val(); + + zv::Val calleeTemplateTypeMap = pt_parameters_acceptor_call(acceptor, PT_PA_GET_TEMPLATE_TYPE_MAP); + if (UNEXPECTED(calleeTemplateTypeMap.isUndef())) return zv::Val(); + if (UNEXPECTED(!zv::Ref(calleeTemplateTypeMap.raw()).isObject())) { + zend_type_error("phpstan_turbo: getTemplateTypeMap() must return an object"); + return zv::Val(); + } + zv::Val isEmpty = pt_type_call(Z_OBJ_P(calleeTemplateTypeMap.raw()), PT_LC("isempty"), 0, NULL); + if (UNEXPECTED(isEmpty.isUndef())) return zv::Val(); + if (zend_is_true(isEmpty.raw())) return closureType; + bool references; + if (UNEXPECTED(!referencesCalleeTemplateTypes(closureType.raw(), calleeTemplateTypeMap.raw(), references))) return zv::Val(); + if (!references) return closureType; + + zv::Val siblingAcceptor = paddedMetadataAcceptor(w, false, &i); + if (UNEXPECTED(siblingAcceptor.isUndef())) return zv::Val(); + zv::Val resolvedTemplateTypeMap = pt_parameters_acceptor_call(siblingAcceptor.raw(), PT_PA_GET_RESOLVED_TEMPLATE_TYPE_MAP); + if (UNEXPECTED(resolvedTemplateTypeMap.isUndef())) return zv::Val(); + + return resolveCalleeTemplateTypes(closureType.raw(), calleeTemplateTypeMap.raw(), resolvedTemplateTypeMap.raw()); + } + /* the closure-type gather ahead of a closure / arrow function argument's * metadata; false = pending exception */ zend_never_inline bool gatherClosureArg(Walk &w, ArgRecord &record, zval *value) const @@ -2436,7 +2579,7 @@ class ArgumentsHandler // the evolving scope, before the body is processed zval *originalArgForGather = originalArgOf(record.arg); zv::Val gathered = w.typeDrivenAcceptorSelection - ? gatherClosureArgType(Z_ARRVAL_P(w.parametersAcceptors), record.key, value, w.scope.raw()) + ? gatherClosureArgType(w, record.key, value) : closureTypeResolverGetClosureType(slot(slots::closureTypeResolver), w.scope.raw(), value, true, w.storage); if (UNEXPECTED(gathered.isUndef())) return false; zval_ptr_dtor(&record.gathered); diff --git a/turbo-ext/src/generated/ArgumentsHandler.h b/turbo-ext/src/generated/ArgumentsHandler.h index d8e3af1f82b..b9e44b6aaa3 100644 --- a/turbo-ext/src/generated/ArgumentsHandler.h +++ b/turbo-ext/src/generated/ArgumentsHandler.h @@ -116,41 +116,49 @@ inline constexpr char strings[] = "value\0" /* 1382 */ "PhpParser\\Node\\Expr\0" /* 1388 */ "argConsumesResolvedParameterType\0" /* 1408 */ - "closureExpr\0" /* 1441 */ - "gatherClosureArgType\0" /* 1453 */ - "args\0" /* 1474 */ - "PHPStan\\Reflection\\ParametersAcceptor\0" /* 1479 */ - "selectCountStableMetadataAcceptor\0" /* 1517 */ - "acceptor\0" /* 1551 */ - "arg\0" /* 1560 */ - "PHPStan\\Reflection\\ParameterReflection\0" /* 1564 */ - "matchArgParameter\0" /* 1603 */ - "selectArgsAcceptor\0" /* 1621 */ - "gatheredTypes\0" /* 1640 */ - "selectArgsMetadataAcceptor\0" /* 1654 */ - "originalCall\0" /* 1681 */ - "normalizedCall\0" /* 1694 */ - "processDroppedArgs\0" /* 1709 */ - "parameter\0" /* 1728 */ - "parameterType\0" /* 1738 */ - "callCallbackImmediately\0" /* 1752 */ - "shouldInvalidateCallbackExpressions\0" /* 1776 */ - "getParameterTypeFromParameterClosureTypeExtension\0" /* 1812 */ - "currentParameter\0" /* 1862 */ - "getParameterOutExtensionsType\0" /* 1879 */ - "call\0" /* 1909 */ - "resolveClosureThisType\0" /* 1914 */ - "position\0" /* 1937 */ - "unpackedType\0" /* 1946 */ - "isPure\0" /* 1959 */ - "observeUnpackedArgument\0" /* 1966 */ - "valueType\0" /* 1990 */ - "observeArgumentValue\0" /* 2000 */ - "findOriginalParameterType\0" /* 2021 */ - "argResults\0" /* 2047 */ - "argValue\0" /* 2058 */ - "PHPStan\\Analyser\\ExpressionResult\0" /* 2067 */ - "readArgResult"; /* 2101 */ + "args\0" /* 1441 */ + "gatheredArgTypeByIndex\0" /* 1446 */ + "closureExpr\0" /* 1469 */ + "gatherClosureArgType\0" /* 1481 */ + "calleeTemplateTypeMap\0" /* 1502 */ + "PHPStan\\Type\\Generic\\TemplateTypeMap\0" /* 1524 */ + "referencesCalleeTemplateTypes\0" /* 1561 */ + "resolvedTemplateTypeMap\0" /* 1591 */ + "resolveCalleeTemplateTypes\0" /* 1615 */ + "PHPStan\\Type\\Generic\\TemplateType\0" /* 1642 */ + "isCalleeTemplateType\0" /* 1676 */ + "PHPStan\\Reflection\\ParametersAcceptor\0" /* 1697 */ + "selectCountStableMetadataAcceptor\0" /* 1735 */ + "acceptor\0" /* 1769 */ + "arg\0" /* 1778 */ + "PHPStan\\Reflection\\ParameterReflection\0" /* 1782 */ + "matchArgParameter\0" /* 1821 */ + "selectArgsAcceptor\0" /* 1839 */ + "gatheredTypes\0" /* 1858 */ + "selectArgsMetadataAcceptor\0" /* 1872 */ + "originalCall\0" /* 1899 */ + "normalizedCall\0" /* 1912 */ + "processDroppedArgs\0" /* 1927 */ + "parameter\0" /* 1946 */ + "parameterType\0" /* 1956 */ + "callCallbackImmediately\0" /* 1970 */ + "shouldInvalidateCallbackExpressions\0" /* 1994 */ + "getParameterTypeFromParameterClosureTypeExtension\0" /* 2030 */ + "currentParameter\0" /* 2080 */ + "getParameterOutExtensionsType\0" /* 2097 */ + "call\0" /* 2127 */ + "resolveClosureThisType\0" /* 2132 */ + "position\0" /* 2155 */ + "unpackedType\0" /* 2164 */ + "isPure\0" /* 2177 */ + "observeUnpackedArgument\0" /* 2184 */ + "valueType\0" /* 2208 */ + "observeArgumentValue\0" /* 2218 */ + "findOriginalParameterType\0" /* 2239 */ + "argResults\0" /* 2265 */ + "argValue\0" /* 2276 */ + "PHPStan\\Analyser\\ExpressionResult\0" /* 2285 */ + "readArgResult"; /* 2319 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 25), /* __construct $templateArgumentObserver */ reg::packed(76, 0, 100), /* __construct $expressionResultFactory */ @@ -190,83 +198,97 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1245, MAY_BE_VOID), /* addGatheredArgType return */ reg::packed(1382, 0, 1388), /* argConsumesResolvedParameterType $value */ reg::packed(1245, MAY_BE_BOOL), /* argConsumesResolvedParameterType return */ + reg::packed(832, 0, 850), /* gatherClosureArgType $nodeScopeResolver */ + reg::packed(1441, MAY_BE_ARRAY), /* gatherClosureArgType $args */ reg::packed(993, MAY_BE_ARRAY), /* gatherClosureArgType $parametersAcceptors */ + reg::packed(1013, MAY_BE_NULL | MAY_BE_ARRAY), /* gatherClosureArgType $namedArgumentsVariants */ + reg::packed(1446, MAY_BE_ARRAY), /* gatherClosureArgType $gatheredArgTypeByIndex */ reg::packed(1338, MAY_BE_LONG), /* gatherClosureArgType $i */ - reg::packed(1441, 0, 1388), /* gatherClosureArgType $closureExpr */ + reg::packed(1469, 0, 1388), /* gatherClosureArgType $closureExpr */ reg::packed(1074, 0, 1080), /* gatherClosureArgType $scope */ reg::packed(1245, 0, 1345), /* gatherClosureArgType return */ + reg::packed(1340, 0, 1345), /* referencesCalleeTemplateTypes $type */ + reg::packed(1502, 0, 1524), /* referencesCalleeTemplateTypes $calleeTemplateTypeMap */ + reg::packed(1245, MAY_BE_BOOL), /* referencesCalleeTemplateTypes return */ + reg::packed(1340, 0, 1345), /* resolveCalleeTemplateTypes $type */ + reg::packed(1502, 0, 1524), /* resolveCalleeTemplateTypes $calleeTemplateTypeMap */ + reg::packed(1591, 0, 1524), /* resolveCalleeTemplateTypes $resolvedTemplateTypeMap */ + reg::packed(1245, 0, 1345), /* resolveCalleeTemplateTypes return */ + reg::packed(1340, 0, 1642), /* isCalleeTemplateType $type */ + reg::packed(1502, 0, 1524), /* isCalleeTemplateType $calleeTemplateTypeMap */ + reg::packed(1245, MAY_BE_BOOL), /* isCalleeTemplateType return */ reg::packed(832, 0, 850), /* selectCountStableMetadataAcceptor $nodeScopeResolver */ - reg::packed(1474, MAY_BE_ARRAY), /* selectCountStableMetadataAcceptor $args */ + reg::packed(1441, MAY_BE_ARRAY), /* selectCountStableMetadataAcceptor $args */ reg::packed(993, MAY_BE_ARRAY), /* selectCountStableMetadataAcceptor $parametersAcceptors */ reg::packed(1013, MAY_BE_NULL | MAY_BE_ARRAY), /* selectCountStableMetadataAcceptor $namedArgumentsVariants */ reg::packed(1074, 0, 1080), /* selectCountStableMetadataAcceptor $scope */ - reg::packed(1245, 0, 1479), /* selectCountStableMetadataAcceptor return */ - reg::packed(1551, 0, 1479), /* matchArgParameter $acceptor */ - reg::packed(1560, 0, 1319), /* matchArgParameter $arg */ + reg::packed(1245, 0, 1697), /* selectCountStableMetadataAcceptor return */ + reg::packed(1769, 0, 1697), /* matchArgParameter $acceptor */ + reg::packed(1778, 0, 1319), /* matchArgParameter $arg */ reg::packed(1338, MAY_BE_LONG), /* matchArgParameter $i */ - reg::packed(1245, MAY_BE_NULL, 1564), /* matchArgParameter return */ + reg::packed(1245, MAY_BE_NULL, 1782), /* matchArgParameter return */ reg::packed(1286, MAY_BE_ARRAY), /* selectArgsAcceptor $types */ reg::packed(993, MAY_BE_ARRAY), /* selectArgsAcceptor $parametersAcceptors */ reg::packed(1013, MAY_BE_NULL | MAY_BE_ARRAY), /* selectArgsAcceptor $namedArgumentsVariants */ reg::packed(1299, MAY_BE_BOOL), /* selectArgsAcceptor $hasName */ reg::packed(1292, MAY_BE_BOOL), /* selectArgsAcceptor $unpack */ - reg::packed(1245, 0, 1479), /* selectArgsAcceptor return */ + reg::packed(1245, 0, 1697), /* selectArgsAcceptor return */ reg::packed(832, 0, 850), /* selectArgsMetadataAcceptor $nodeScopeResolver */ - reg::packed(1474, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $args */ - reg::packed(1640, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $gatheredTypes */ + reg::packed(1441, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $args */ + reg::packed(1858, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $gatheredTypes */ reg::packed(993, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $parametersAcceptors */ reg::packed(1013, MAY_BE_NULL | MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $namedArgumentsVariants */ reg::packed(1299, MAY_BE_BOOL), /* selectArgsMetadataAcceptor $hasName */ reg::packed(1292, MAY_BE_BOOL), /* selectArgsMetadataAcceptor $unpack */ reg::packed(1074, 0, 1080), /* selectArgsMetadataAcceptor $scope */ - reg::packed(1245, 0, 1479), /* selectArgsMetadataAcceptor return */ + reg::packed(1245, 0, 1697), /* selectArgsMetadataAcceptor return */ reg::packed(832, 0, 850), /* processDroppedArgs $nodeScopeResolver */ reg::packed(885, 0, 890), /* processDroppedArgs $stmt */ - reg::packed(1681, 0, 1045), /* processDroppedArgs $originalCall */ - reg::packed(1694, 0, 1045), /* processDroppedArgs $normalizedCall */ + reg::packed(1899, 0, 1045), /* processDroppedArgs $originalCall */ + reg::packed(1912, 0, 1045), /* processDroppedArgs $normalizedCall */ reg::packed(1074, 0, 1080), /* processDroppedArgs $scope */ reg::packed(1111, 0, 1119), /* processDroppedArgs $storage */ reg::packed(1173, 0, 1181), /* processDroppedArgs $context */ reg::packed(1245, MAY_BE_VOID), /* processDroppedArgs return */ - reg::packed(1728, MAY_BE_NULL, 1564), /* callCallbackImmediately $parameter */ - reg::packed(1738, MAY_BE_NULL, 1345), /* callCallbackImmediately $parameterType */ + reg::packed(1946, MAY_BE_NULL, 1782), /* callCallbackImmediately $parameter */ + reg::packed(1956, MAY_BE_NULL, 1345), /* callCallbackImmediately $parameterType */ reg::packed(910, 0), /* callCallbackImmediately $calleeReflection */ reg::packed(1245, MAY_BE_BOOL), /* callCallbackImmediately return */ - reg::packed(1728, MAY_BE_NULL, 1564), /* shouldInvalidateCallbackExpressions $parameter */ + reg::packed(1946, MAY_BE_NULL, 1782), /* shouldInvalidateCallbackExpressions $parameter */ reg::packed(1245, MAY_BE_BOOL), /* shouldInvalidateCallbackExpressions return */ reg::packed(1036, 0, 1045), /* getParameterTypeFromParameterClosureTypeExtension $callLike */ reg::packed(910, 0), /* getParameterTypeFromParameterClosureTypeExtension $calleeReflection */ - reg::packed(1728, 0, 1564), /* getParameterTypeFromParameterClosureTypeExtension $parameter */ + reg::packed(1946, 0, 1782), /* getParameterTypeFromParameterClosureTypeExtension $parameter */ reg::packed(1074, 0, 1080), /* getParameterTypeFromParameterClosureTypeExtension $scope */ reg::packed(1245, MAY_BE_NULL, 1345), /* getParameterTypeFromParameterClosureTypeExtension return */ reg::packed(1036, 0, 1045), /* getParameterOutExtensionsType $callLike */ reg::packed(910, 0), /* getParameterOutExtensionsType $calleeReflection */ - reg::packed(1862, 0, 1564), /* getParameterOutExtensionsType $currentParameter */ + reg::packed(2080, 0, 1782), /* getParameterOutExtensionsType $currentParameter */ reg::packed(1074, 0, 1080), /* getParameterOutExtensionsType $scope */ reg::packed(1245, MAY_BE_NULL, 1345), /* getParameterOutExtensionsType return */ - reg::packed(1909, MAY_BE_NULL, 1045), /* resolveClosureThisType $call */ + reg::packed(2127, MAY_BE_NULL, 1045), /* resolveClosureThisType $call */ reg::packed(910, 0), /* resolveClosureThisType $calleeReflection */ - reg::packed(1728, 0, 1564), /* resolveClosureThisType $parameter */ + reg::packed(1946, 0, 1782), /* resolveClosureThisType $parameter */ reg::packed(1074, 0, 1080), /* resolveClosureThisType $scope */ reg::packed(1245, MAY_BE_NULL, 1345), /* resolveClosureThisType return */ reg::packed(1074, 0, 1080), /* observeUnpackedArgument $scope */ - reg::packed(1551, 0, 1479), /* observeUnpackedArgument $acceptor */ - reg::packed(1937, MAY_BE_LONG), /* observeUnpackedArgument $position */ - reg::packed(1946, 0, 1345), /* observeUnpackedArgument $unpackedType */ - reg::packed(1959, MAY_BE_BOOL), /* observeUnpackedArgument $isPure */ + reg::packed(1769, 0, 1697), /* observeUnpackedArgument $acceptor */ + reg::packed(2155, MAY_BE_LONG), /* observeUnpackedArgument $position */ + reg::packed(2164, 0, 1345), /* observeUnpackedArgument $unpackedType */ + reg::packed(2177, MAY_BE_BOOL), /* observeUnpackedArgument $isPure */ reg::packed(1245, 0, 1080), /* observeUnpackedArgument return */ reg::packed(1074, 0, 1080), /* observeArgumentValue $scope */ - reg::packed(1551, 0, 1479), /* observeArgumentValue $acceptor */ - reg::packed(1728, 0, 1564), /* observeArgumentValue $parameter */ - reg::packed(1990, 0, 1345), /* observeArgumentValue $valueType */ - reg::packed(1959, MAY_BE_BOOL), /* observeArgumentValue $isPure */ + reg::packed(1769, 0, 1697), /* observeArgumentValue $acceptor */ + reg::packed(1946, 0, 1782), /* observeArgumentValue $parameter */ + reg::packed(2208, 0, 1345), /* observeArgumentValue $valueType */ + reg::packed(2177, MAY_BE_BOOL), /* observeArgumentValue $isPure */ reg::packed(1245, 0, 1080), /* observeArgumentValue return */ - reg::packed(1551, 0, 1479), /* findOriginalParameterType $acceptor */ - reg::packed(1728, 0, 1564), /* findOriginalParameterType $parameter */ + reg::packed(1769, 0, 1697), /* findOriginalParameterType $acceptor */ + reg::packed(1946, 0, 1782), /* findOriginalParameterType $parameter */ reg::packed(1245, MAY_BE_NULL, 1345), /* findOriginalParameterType return */ - reg::packed(2047, MAY_BE_ARRAY), /* readArgResult $argResults */ - reg::packed(2058, 0, 1388), /* readArgResult $argValue */ - reg::packed(1245, 0, 2067), /* readArgResult return */ + reg::packed(2265, MAY_BE_ARRAY), /* readArgResult $argResults */ + reg::packed(2276, 0, 1388), /* readArgResult $argValue */ + reg::packed(1245, 0, 2285), /* readArgResult return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -277,21 +299,24 @@ inline constexpr sigtab::Sig __construct = { { 820 /* __construct */, 16, 0, 16, inline constexpr sigtab::Sig processArgs = { { 1274 /* processArgs */, 11, 16, 12, 28, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig addGatheredArgType = { { 1363 /* addGatheredArgType */, 6, 29, 6, 35, ZEND_ACC_PRIVATE } }; inline constexpr sigtab::Sig argConsumesResolvedParameterType = { { 1408 /* argConsumesResolvedParameterType */, 1, 36, 1, 37, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig gatherClosureArgType = { { 1453 /* gatherClosureArgType */, 4, 38, 4, 42, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig selectCountStableMetadataAcceptor = { { 1517 /* selectCountStableMetadataAcceptor */, 5, 43, 5, 48, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig matchArgParameter = { { 1603 /* matchArgParameter */, 3, 49, 3, 52, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig selectArgsAcceptor = { { 1621 /* selectArgsAcceptor */, 5, 53, 5, 58, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig selectArgsMetadataAcceptor = { { 1654 /* selectArgsMetadataAcceptor */, 8, 59, 8, 67, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processDroppedArgs = { { 1709 /* processDroppedArgs */, 7, 68, 7, 75, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig callCallbackImmediately = { { 1752 /* callCallbackImmediately */, 3, 76, 3, 79, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig shouldInvalidateCallbackExpressions = { { 1776 /* shouldInvalidateCallbackExpressions */, 1, 80, 1, 81, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getParameterTypeFromParameterClosureTypeExtension = { { 1812 /* getParameterTypeFromParameterClosureTypeExtension */, 4, 82, 4, 86, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getParameterOutExtensionsType = { { 1879 /* getParameterOutExtensionsType */, 4, 87, 4, 91, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig resolveClosureThisType = { { 1914 /* resolveClosureThisType */, 4, 92, 4, 96, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig observeUnpackedArgument = { { 1966 /* observeUnpackedArgument */, 5, 97, 5, 102, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig observeArgumentValue = { { 2000 /* observeArgumentValue */, 5, 103, 5, 108, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig findOriginalParameterType = { { 2021 /* findOriginalParameterType */, 2, 109, 2, 111, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig readArgResult = { { 2101 /* readArgResult */, 2, 112, 2, 114, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig gatherClosureArgType = { { 1481 /* gatherClosureArgType */, 8, 38, 8, 46, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig referencesCalleeTemplateTypes = { { 1561 /* referencesCalleeTemplateTypes */, 2, 47, 2, 49, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig resolveCalleeTemplateTypes = { { 1615 /* resolveCalleeTemplateTypes */, 3, 50, 3, 53, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig isCalleeTemplateType = { { 1676 /* isCalleeTemplateType */, 2, 54, 2, 56, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig selectCountStableMetadataAcceptor = { { 1735 /* selectCountStableMetadataAcceptor */, 5, 57, 5, 62, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig matchArgParameter = { { 1821 /* matchArgParameter */, 3, 63, 3, 66, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig selectArgsAcceptor = { { 1839 /* selectArgsAcceptor */, 5, 67, 5, 72, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig selectArgsMetadataAcceptor = { { 1872 /* selectArgsMetadataAcceptor */, 8, 73, 8, 81, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processDroppedArgs = { { 1927 /* processDroppedArgs */, 7, 82, 7, 89, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callCallbackImmediately = { { 1970 /* callCallbackImmediately */, 3, 90, 3, 93, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig shouldInvalidateCallbackExpressions = { { 1994 /* shouldInvalidateCallbackExpressions */, 1, 94, 1, 95, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getParameterTypeFromParameterClosureTypeExtension = { { 2030 /* getParameterTypeFromParameterClosureTypeExtension */, 4, 96, 4, 100, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getParameterOutExtensionsType = { { 2097 /* getParameterOutExtensionsType */, 4, 101, 4, 105, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig resolveClosureThisType = { { 2132 /* resolveClosureThisType */, 4, 106, 4, 110, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeUnpackedArgument = { { 2184 /* observeUnpackedArgument */, 5, 111, 5, 116, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeArgumentValue = { { 2218 /* observeArgumentValue */, 5, 117, 5, 122, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig findOriginalParameterType = { { 2239 /* findOriginalParameterType */, 2, 123, 2, 125, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig readArgResult = { { 2319 /* readArgResult */, 2, 126, 2, 128, ZEND_ACC_PRIVATE } }; } // namespace sig } // namespace ptdecl::ArgumentsHandler