From 9ef0fc8403a009143b56479027538c0d9026e3fb Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Sat, 7 Nov 2026 11:09:14 +0100 Subject: [PATCH 1/2] Resolve the callee's own templates in a closure type gathered against the raw parameter A closure argument is gathered with the raw callable(T): T parameter pushed, so a closure returning its parameter reads back the callee's own T (string&T with a native string type). That type then took part in the inference of T, which kept the self-reference: the closure parameter was 'a'|'b'|(string&T), and an exhaustive match on it was reported as unhandled. The callee's own templates in the gathered type are now resolved from the other arguments gathered so far, falling back to the template's default or bound when they say nothing about it or pass no value of it (an empty array). The gather is unchanged when the closure type does not reference them. Mirrored in the native ArgumentsHandler. Co-Authored-By: Claude Opus 5.5 --- src/Analyser/ArgumentsHandler.php | 95 +++++++- tests/PHPStan/Analyser/nsrt/bug-15654.php | 105 +++++++++ .../Comparison/MatchExpressionRuleTest.php | 12 + .../Rules/Comparison/data/bug-15654.php | 22 ++ turbo-ext/src/ArgumentsHandler.cpp | 223 ++++++++++++++---- turbo-ext/src/generated/ArgumentsHandler.h | 189 ++++++++------- 6 files changed, 519 insertions(+), 127 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-15654.php create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-15654.php 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 From 0c803681567cb0d5dd0c483fe24805d992193926 Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Sat, 7 Nov 2026 13:47:59 +0100 Subject: [PATCH 2/2] Resolve the templates a closure argument only receives before typing it A closure argument was gathered against the raw callable(T): U parameter, so while its body was typed, its parameter was the callee's own T. An offset or a property read off it was mixed, since T says nothing about what it will be, and U was inferred from that mixed: map($rows, fn (array $row) => $row['name']) returned list. The templates the callable only passes to the closure - referenced by its parameters, not by its return type - are now resolved from the other arguments gathered so far before the closure is typed. The ones in its return type stay raw, so the closure's own return still takes part in their inference. Mirrored in the native ArgumentsHandler. Two assertions recorded the old result: bug-11861 now gets the array its filtering closure returns, and an untyped closure parameter in generics.php now gets the int its sibling argument passes, as the natively typed one next to it already did. Co-Authored-By: Claude Opus 5.5 --- src/Analyser/ArgumentsHandler.php | 156 +++++++++++++- tests/PHPStan/Analyser/nsrt/bug-11861.php | 2 +- tests/PHPStan/Analyser/nsrt/bug-14885.php | 190 +++++++++++++++++ tests/PHPStan/Analyser/nsrt/generics.php | 2 +- turbo-ext/src/ArgumentsHandler.cpp | 233 ++++++++++++++++++++- turbo-ext/src/generated/ArgumentsHandler.h | 203 ++++++++++-------- 6 files changed, 679 insertions(+), 107 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-14885.php diff --git a/src/Analyser/ArgumentsHandler.php b/src/Analyser/ArgumentsHandler.php index 5b38d47d033..24b022beb1d 100644 --- a/src/Analyser/ArgumentsHandler.php +++ b/src/Analyser/ArgumentsHandler.php @@ -35,6 +35,8 @@ use PHPStan\Reflection\ParameterReflection; use PHPStan\Reflection\ParametersAcceptor; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Reflection\Php\DummyParameter; +use PHPStan\Reflection\Php\ExtendedDummyParameter; use PHPStan\Reflection\ResolvedFunctionVariant; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; @@ -48,6 +50,7 @@ use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Generic\TemplateTypeVariance; +use PHPStan\Type\Generic\TemplateTypeVarianceMap; use PHPStan\Type\MethodParameterClosureThisExtension; use PHPStan\Type\MethodParameterClosureTypeExtension; use PHPStan\Type\MethodParameterOutTypeExtension; @@ -63,6 +66,7 @@ use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeUtils; +use PHPStan\Type\UnionType; use function array_keys; use function array_last; use function array_map; @@ -954,11 +958,14 @@ private function argConsumesResolvedParameterType(Expr $value): bool /** * Resolves the type of a closure/arrow function argument for the generic * gather, mirroring ParametersAcceptorSelector::selectFromArgs(): the closure - * type is read with the RAW (un-generic-resolved) acceptor parameter pushed - * onto the in-function-call stack, so its body sees the template parameter - * (effectively mixed for an untyped param) rather than a parameter already - * resolved from sibling args. That keeps the inferred return type (the U in - * callable(T): U) faithful to the closure's own declaration. + * type is read with the acceptor parameter pushed onto the in-function-call + * stack before its templates are resolved, so the inferred return type (the + * U in callable(T): U) stays faithful to the closure's own declaration. + * + * The templates the closure only receives (the T in callable(T): U) are + * resolved from the other arguments gathered so far before the push: left + * as T, an offset or a property read off the closure's parameter is mixed, + * and U would be inferred from that mixed (see resolveReceivedTemplateTypes()). * * 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 @@ -995,9 +1002,17 @@ private function gatherClosureArgType( return $this->closureParameterResolver->resolveCallableTypeForScope($closureExpr, $scope); } + $siblingTemplateTypeMap = null; + $pushedParameter = $rawParameter; + $receivedTemplateNames = self::getReceivedTemplateNames($rawParameter->getType(), $scope); + if ($receivedTemplateNames !== []) { + $siblingTemplateTypeMap = $this->selectSiblingTemplateTypeMap($nodeScopeResolver, $args, $parametersAcceptors, $namedArgumentsVariants, $gatheredArgTypeByIndex, $i, $scope); + $pushedParameter = self::resolveReceivedTemplateTypes($rawParameter, $receivedTemplateNames, $siblingTemplateTypeMap); + } + $closureType = $this->closureParameterResolver->resolveCallableTypeForScope( $closureExpr, - $scope->pushInFunctionCall(null, $rawParameter, false), + $scope->pushInFunctionCall(null, $pushedParameter, false), ); $calleeTemplateTypeMap = $parametersAcceptors[0]->getTemplateTypeMap(); @@ -1005,6 +1020,32 @@ private function gatherClosureArgType( return $closureType; } + return self::resolveCalleeTemplateTypes( + $closureType, + $calleeTemplateTypeMap, + $siblingTemplateTypeMap ?? $this->selectSiblingTemplateTypeMap($nodeScopeResolver, $args, $parametersAcceptors, $namedArgumentsVariants, $gatheredArgTypeByIndex, $i, $scope), + ); + } + + /** + * The template types resolved from every argument gathered so far except + * the i-th one, which counts as mixed. + * + * @param Node\Arg[] $args + * @param ParametersAcceptor[] $parametersAcceptors + * @param ParametersAcceptor[]|null $namedArgumentsVariants + * @param array $gatheredArgTypeByIndex + */ + private function selectSiblingTemplateTypeMap( + NodeScopeResolver $nodeScopeResolver, + array $args, + array $parametersAcceptors, + ?array $namedArgumentsVariants, + array $gatheredArgTypeByIndex, + int $i, + MutatingScope $scope, + ): TemplateTypeMap + { $siblingTypes = []; $siblingUnpack = false; $siblingHasName = false; @@ -1013,10 +1054,105 @@ private function gatherClosureArgType( $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(), + return $this->selectArgsMetadataAcceptor($nodeScopeResolver, $args, $siblingTypes, $parametersAcceptors, $namedArgumentsVariants, $siblingHasName, $siblingUnpack, $scope)->getResolvedTemplateTypeMap(); + } + + /** + * The names of the template types a callable parameter type passes to the + * callable without expecting them back: those its parameters reference and + * its return type does not. A template in the return type is left alone, as + * the closure's own return takes part in its inference. + * + * @return list + */ + private static function getReceivedTemplateNames(Type $parameterType, MutatingScope $scope): array + { + if (!$parameterType->hasTemplateOrLateResolvableType() || $parameterType->isCallable()->no()) { + return []; + } + + $callableType = $parameterType; + if ($callableType instanceof UnionType) { + $callableType = $callableType->filterTypes(static fn (Type $innerType): bool => $innerType->isCallable()->yes()); + } + if (!$callableType->isCallable()->yes()) { + return []; + } + + $returnedTemplateNames = []; + foreach ($callableType->getCallableParametersAcceptors($scope) as $acceptor) { + foreach ($acceptor->getReturnType()->getReferencedTemplateTypes(TemplateTypeVariance::createCovariant()) as $reference) { + $returnedTemplateNames[$reference->getType()->getName()] = true; + } + } + + $receivedTemplateNames = []; + foreach ($callableType->getReferencedTemplateTypes(TemplateTypeVariance::createInvariant()) as $reference) { + $name = $reference->getType()->getName(); + if (isset($returnedTemplateNames[$name])) { + continue; + } + $receivedTemplateNames[$name] = true; + } + + return array_keys($receivedTemplateNames); + } + + /** + * The parameter with the received template types replaced by what the other + * arguments resolve them to - left alone when they say nothing about them, + * or pass no value of them (never, from an empty array). + * + * @param list $receivedTemplateNames + */ + private static function resolveReceivedTemplateTypes(ParameterReflection $parameter, array $receivedTemplateNames, TemplateTypeMap $siblingTemplateTypeMap): ParameterReflection + { + $receivedTypes = []; + foreach ($receivedTemplateNames as $name) { + $resolvedType = $siblingTemplateTypeMap->getType($name); + if ($resolvedType === null || $resolvedType instanceof ErrorType || $resolvedType instanceof NeverType) { + continue; + } + $receivedTypes[$name] = $resolvedType; + } + if ($receivedTypes === []) { + return $parameter; + } + + $type = TemplateTypeHelper::resolveTemplateTypes( + $parameter->getType(), + new TemplateTypeMap($receivedTypes), + TemplateTypeVarianceMap::createEmpty(), + TemplateTypeVariance::createInvariant(), + ); + + if (!$parameter instanceof ExtendedParameterReflection) { + return new DummyParameter( + $parameter->getName(), + $type, + $parameter->isOptional(), + $parameter->passedByReference(), + $parameter->isVariadic(), + $parameter->getDefaultValue(), + ); + } + + return new ExtendedDummyParameter( + $parameter->getName(), + $type, + $parameter->isOptional(), + $parameter->passedByReference(), + $parameter->isVariadic(), + $parameter->getDefaultValue(), + $parameter->getNativeType(), + $type, + $parameter->getOutType(), + $parameter->isImmediatelyInvokedCallable(), + $parameter->getClosureThisType(), + $parameter->getAttributes(), + $parameter->getAllowedConstants(), + $parameter->isPureUnlessCallableIsImpureParameter(), + $parameter->isPureUnlessParameterPassedParameter(), ); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-11861.php b/tests/PHPStan/Analyser/nsrt/bug-11861.php index a3e4addc62d..dd311255b26 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-11861.php +++ b/tests/PHPStan/Analyser/nsrt/bug-11861.php @@ -55,5 +55,5 @@ function filterArrayNotNull(array $source): array filterArrayNotNull(...) ); -assertType('array, array>', $mappedA); +assertType('array, array>', $mappedA); assertType('array, array>', $mappedAWithFirstClassSyntax); diff --git a/tests/PHPStan/Analyser/nsrt/bug-14885.php b/tests/PHPStan/Analyser/nsrt/bug-14885.php new file mode 100644 index 00000000000..6ae21e1748b --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14885.php @@ -0,0 +1,190 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug14885; + +use Closure; +use function PHPStan\Testing\assertType; + +/** + * @template Tv + * @template T + * + * @param iterable $iterable + * @param (Closure(Tv): T) $function + * + * @return list + */ +function map(iterable $iterable, Closure $function): array +{ + $result = []; + foreach ($iterable as $value) { + $result[] = $function($value); + } + + return $result; +} + +function issue(): void +{ + assertType( + "list}>", + map(['a', 'b', 'c'], fn ($a) => [$a, ord($a)]), + ); + + assertType( + "list", + map([['a', 'b'], ['c', 'd']], fn ($x) => [$x[0], $x[1]]), + ); +} + +/** + * @template K of array-key + * @template T + * @template L of array-key + * @template U + * + * @param array $array + * @param callable(T, K): iterable $fn + * + * @return array + */ +function mapWithKeys(array $array, callable $fn): array +{ + return []; +} + +/** + * @template T + * @template U + * + * @param callable(T): U $fn + * @param array $array + * + * @return list + */ +function mapCallableFirst(callable $fn, array $array): array +{ + return []; +} + +/** + * @template T + * @template U + * + * @param array $array + * @param (callable(T): U)|null $fn + * + * @return list + */ +function mapNullable(array $array, ?callable $fn): array +{ + return []; +} + +/** + * @template T + * @template TAcc + * + * @param array $array + * @param callable(TAcc, T): TAcc $fn + * @param TAcc $initial + * + * @return TAcc + */ +function reduce(array $array, callable $fn, mixed $initial): mixed +{ + return $initial; +} + +final class Mapper +{ + + /** + * @template T + * @template U + * + * @param array $array + * @param callable(T): U $fn + * + * @return list + */ + public function map(array $array, callable $fn): array + { + return []; + } + + /** + * @template T + * @template U + * + * @param array $array + * @param callable(T): U $fn + * + * @return list + */ + public static function staticMap(array $array, callable $fn): array + { + return []; + } + +} + +/** + * @template T + * @template U + */ +final class Mapped +{ + + /** + * @param array $array + * @param callable(T): U $fn + */ + public function __construct(public array $array, public $fn) + { + } + + /** @return U */ + public function first(): mixed + { + throw new \LogicException(); + } + +} + +/** + * @param list $rows + * @param list> $nested + * @param list<\stdClass&object{name: string}> $objects + */ +function natively(array $rows, array $nested, array $objects, Mapper $mapper): void +{ + assertType('array<1, string>', mapWithKeys($rows, static fn (array $row): iterable => yield 1 => $row['name'])); + assertType('array', mapWithKeys($rows, static fn (array $row): iterable => yield $row['id'] => $row['name'])); + assertType('array', mapWithKeys($rows, static function (array $row): iterable { + $id = $row['id']; + yield $id => $row['name']; + })); + assertType('list', map($objects, static fn (object $o) => $o->name)); + + assertType('list', mapCallableFirst(static fn (array $row) => $row['name'], $rows)); + assertType('list', mapNullable($rows, static fn (array $row) => $row['name'])); + assertType('list', map(function: static fn (array $row) => $row['name'], iterable: $rows)); + assertType('list>', map($nested, static fn (array $list) => map($list, static fn (array $row) => $row['id']))); + assertType('int', reduce($rows, static fn (int $carry, array $row) => $carry + $row['id'], 0)); + + assertType('list', $mapper->map($rows, static fn (array $row) => $row['name'])); + assertType('list', Mapper::staticMap($rows, static fn (array $row) => $row['name'])); + assertType('string', (new Mapped($rows, static fn (array $row) => $row['name']))->first()); +} + +/** + * @template X of array{id: int} + * @param list $rows + */ +function callerTemplate(array $rows): void +{ + assertType('list', map($rows, static fn (array $row) => $row['id'])); +} diff --git a/tests/PHPStan/Analyser/nsrt/generics.php b/tests/PHPStan/Analyser/nsrt/generics.php index 6e150f33349..68f2908140c 100644 --- a/tests/PHPStan/Analyser/nsrt/generics.php +++ b/tests/PHPStan/Analyser/nsrt/generics.php @@ -156,7 +156,7 @@ function testF($arrayOfInt, $callableOrNull) assertType('Closure(mixed): string', function ($a): string { return (string)$a; }); - assertType('array', f($arrayOfInt, function ($a): string { + assertType('array', f($arrayOfInt, function ($a): string { return (string)$a; })); assertType('array', f($arrayOfInt, function ($a) { diff --git a/turbo-ext/src/ArgumentsHandler.cpp b/turbo-ext/src/ArgumentsHandler.cpp index 95a1b55c23c..bd59698bc9b 100644 --- a/turbo-ext/src/ArgumentsHandler.cpp +++ b/turbo-ext/src/ArgumentsHandler.cpp @@ -949,6 +949,202 @@ zv::Val resolveCalleeTemplateTypes(zval *type, zval *calleeTemplateTypeMap, zval /* }}} */ +/* {{{ the template types a closure only receives */ + +/* static fn (Type $innerType): bool => $innerType->isCallable()->yes() */ +void receivedTemplateNamesCallableFilterBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + (void) captures; + if (UNEXPECTED(argc < 1 || Z_TYPE(argv[0]) != IS_OBJECT)) { + zend_type_error("ArgumentsHandler::getReceivedTemplateNames() filter: expected (Type $innerType)"); + return; + } + zend_long isCallable = pt_type_op_trinary(Z_OBJ(argv[0]), PT_OP_IS_CALLABLE, 0, NULL); + if (UNEXPECTED(isCallable < 0)) return; + ZVAL_BOOL(return_value, isCallable == PT_TRI_YES); +} + +/* the names of the template types $type->getReferencedTemplateTypes($variance) + * references, as keys of out (insertion order kept, value true); false = + * pending exception */ +[[nodiscard]] bool collectReferencedTemplateNames(zval *type, zend_long variance, zv::Arr &out) +{ + zval *positionVariance = pt_template_type_variance_singleton(variance); + if (UNEXPECTED(positionVariance == NULL)) return false; + zv::Val references = pt_type_op(Z_OBJ_P(type), PT_OP_GET_REFERENCED_TEMPLATE_TYPES, 1, positionVariance); + 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, referenceVariance; + if (UNEXPECTED(!pt_template_type_reference_parts(reference.raw(), referenceType, referenceVariance))) return false; + zv::Val name = templateTypeName(referenceType.raw()); + if (UNEXPECTED(name.isUndef())) return false; + out.set(Z_STR_P(name.raw()), zv::Val::boolean(true)); + } + return true; +} + +/* Mirrors ArgumentsHandler::getReceivedTemplateNames(): the names as the + * values of a list; UNDEF = pending exception */ +zv::Val getReceivedTemplateNames(zval *parameterType, zval *scope) +{ + zv::Val hasTemplate = pt_type_op(Z_OBJ_P(parameterType), PT_OP_HAS_TEMPLATE_OR_LATE_RESOLVABLE_TYPE, 0, NULL); + if (UNEXPECTED(hasTemplate.isUndef())) return zv::Val(); + if (!zend_is_true(hasTemplate.raw())) return zv::Val(zv::Arr::empty()); + zend_long isCallable = pt_type_op_trinary(Z_OBJ_P(parameterType), PT_OP_IS_CALLABLE, 0, NULL); + if (UNEXPECTED(isCallable < 0)) return zv::Val(); + if (isCallable == PT_TRI_NO) return zv::Val(zv::Arr::empty()); + + zval *callableType = parameterType; + zv::Val filtered; + if (instanceof_function(Z_OBJCE_P(callableType), pt_ce_union_type)) { + zv::Val filter = pt_native_closure(&receivedTemplateNamesCallableFilterBody); + if (UNEXPECTED(filter.isUndef())) return zv::Val(); + filtered = pt_type_call(Z_OBJ_P(callableType), PT_LC("filtertypes"), 1, filter.raw()); + if (UNEXPECTED(filtered.isUndef())) return zv::Val(); + if (UNEXPECTED(!filtered.ref().isObject())) { + zend_throw_error(NULL, "Call to a member function isCallable() on %s", zend_zval_value_name(filtered.raw())); + return zv::Val(); + } + callableType = filtered.raw(); + } + isCallable = pt_type_op_trinary(Z_OBJ_P(callableType), PT_OP_IS_CALLABLE, 0, NULL); + if (UNEXPECTED(isCallable < 0)) return zv::Val(); + if (isCallable != PT_TRI_YES) return zv::Val(zv::Arr::empty()); + + zv::Val acceptors = pt_type_call(Z_OBJ_P(callableType), PT_LC("getcallableparametersacceptors"), 1, scope); + if (UNEXPECTED(acceptors.isUndef())) return zv::Val(); + if (UNEXPECTED(!acceptors.ref().isArray())) { + zend_type_error("phpstan_turbo: getCallableParametersAcceptors() must return array"); + return zv::Val(); + } + zv::Arr returnedTemplateNames = zv::Arr::empty(); + for (zv::ArrayEntry entry : zv::ArrRef(acceptors.raw())) { + zval *acceptor = entry.value().deref().raw(); + if (UNEXPECTED(Z_TYPE_P(acceptor) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function getReturnType() on %s", zend_zval_value_name(acceptor)); + return zv::Val(); + } + zv::Val returnType = pt_parameters_acceptor_call(acceptor, PT_PA_GET_RETURN_TYPE); + if (UNEXPECTED(returnType.isUndef())) return zv::Val(); + if (UNEXPECTED(!returnType.ref().isObject())) { + zend_type_error("phpstan_turbo: getReturnType() must return an object"); + return zv::Val(); + } + if (UNEXPECTED(!collectReferencedTemplateNames(returnType.raw(), PT_TEMPLATE_TYPE_VARIANCE_COVARIANT, returnedTemplateNames))) return zv::Val(); + } + + zv::Arr referencedTemplateNames = zv::Arr::empty(); + if (UNEXPECTED(!collectReferencedTemplateNames(callableType, PT_TEMPLATE_TYPE_VARIANCE_INVARIANT, referencedTemplateNames))) return zv::Val(); + zv::Arr receivedTemplateNames = zv::Arr::empty(); + for (zv::ArrayEntry entry : zv::ArrRef(referencedTemplateNames.raw())) { + zend_string *name = entry.stringKeyOrNull(); + if (UNEXPECTED(name == NULL)) continue; + if (zend_symtable_find(Z_ARRVAL_P(returnedTemplateNames.raw()), name) != NULL) continue; + receivedTemplateNames.push(zv::Val::adoptString(zend_string_copy(name))); + } + return zv::Val(std::move(receivedTemplateNames)); +} + +/* Mirrors ArgumentsHandler::resolveReceivedTemplateTypes(); UNDEF = pending + * exception */ +zv::Val resolveReceivedTemplateTypes(zval *parameter, zval *receivedTemplateNames, zval *siblingTemplateTypeMap) +{ + zv::Arr receivedTypes = zv::Arr::empty(); + bool anyReceived = false; + for (zv::ArrayEntry entry : zv::ArrRef(receivedTemplateNames)) { + zval *name = entry.value().deref().raw(); + zv::Val resolvedType = pt_template_type_map_get_type(siblingTemplateTypeMap, Z_STR_P(name)); + if (UNEXPECTED(resolvedType.isUndef())) return zv::Val(); + if (resolvedType.isNull()) continue; + 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); + if (isError || isNever) continue; + receivedTypes.set(Z_STR_P(name), std::move(resolvedType)); + anyReceived = true; + } + if (!anyReceived) return zv::Val::copyOf(zv::Ref(parameter)); + + zval receivedMap; + if (UNEXPECTED(!pt_template_type_map_new(&receivedMap, receivedTypes.raw()))) return zv::Val(); + zv::Val receivedMapHold = zv::Val::adopt(receivedMap); + zval emptyVarianceMap; + if (UNEXPECTED(!pt_template_type_variance_map_empty(&emptyVarianceMap))) return zv::Val(); + zv::Val emptyVarianceMapHold = zv::Val::adopt(emptyVarianceMap); + zval *invariant = pt_template_type_variance_singleton(PT_TEMPLATE_TYPE_VARIANCE_INVARIANT); + if (UNEXPECTED(invariant == NULL)) return zv::Val(); + zv::Val parameterType = pt_parameter_reflection_call(parameter, PT_PR_GET_TYPE); + if (UNEXPECTED(parameterType.isUndef())) return zv::Val(); + zv::Val type = pt_type_template_type_helper_resolve_template_types(parameterType.raw(), receivedMapHold.raw(), emptyVarianceMapHold.raw(), invariant, false); + if (UNEXPECTED(type.isUndef())) return zv::Val(); + + bool isExtended = false; + if (UNEXPECTED(!isA(parameter, PT_CLASS_EXTENDED_PARAMETER_REFLECTION, isExtended))) return zv::Val(); + if (!isExtended) { + /* new DummyParameter($parameter->getName(), $type, ...) */ + zv::Val name = pt_parameter_reflection_call(parameter, PT_PR_GET_NAME); + if (UNEXPECTED(name.isUndef())) return zv::Val(); + if (UNEXPECTED(!name.ref().isString())) { + zend_type_error("phpstan_turbo: getName() must return string"); + return zv::Val(); + } + zv::Val optional = pt_parameter_reflection_call(parameter, PT_PR_IS_OPTIONAL); + if (UNEXPECTED(optional.isUndef())) return zv::Val(); + zv::Val passedByReference = pt_parameter_reflection_call(parameter, PT_PR_PASSED_BY_REFERENCE); + if (UNEXPECTED(passedByReference.isUndef())) return zv::Val(); + zv::Val variadic = pt_parameter_reflection_call(parameter, PT_PR_IS_VARIADIC); + if (UNEXPECTED(variadic.isUndef())) return zv::Val(); + zv::Val defaultValue = pt_parameter_reflection_call(parameter, PT_PR_GET_DEFAULT_VALUE); + if (UNEXPECTED(defaultValue.isUndef())) return zv::Val(); + return pt_dummy_parameter_new( + Z_STR_P(name.raw()), + type.raw(), + zend_is_true(optional.raw()), + passedByReference.isNull() ? NULL : passedByReference.raw(), + zend_is_true(variadic.raw()), + defaultValue.isNull() ? NULL : defaultValue.raw() + ); + } + + /* new ExtendedDummyParameter($parameter->getName(), $type, ...) - the + * getters in argument order, $type also standing for the PHPDoc type */ + static constexpr pt_parameter_reflection_member getters[13] = { + PT_PR_GET_NAME, PT_PR_IS_OPTIONAL, PT_PR_PASSED_BY_REFERENCE, PT_PR_IS_VARIADIC, PT_PR_GET_DEFAULT_VALUE, + PT_PR_GET_NATIVE_TYPE, PT_PR_GET_OUT_TYPE, PT_PR_IS_IMMEDIATELY_INVOKED_CALLABLE, PT_PR_GET_CLOSURE_THIS_TYPE, + PT_PR_GET_ATTRIBUTES, PT_PR_GET_ALLOWED_CONSTANTS, PT_PR_IS_PURE_UNLESS_CALLABLE_IS_IMPURE_PARAMETER, + PT_PR_IS_PURE_UNLESS_PARAMETER_PASSED_PARAMETER, + }; + zv::Val values[13]; + for (int k = 0; k < 13; k++) { + values[k] = pt_parameter_reflection_call(parameter, getters[k]); + if (UNEXPECTED(values[k].isUndef())) return zv::Val(); + } + zval args[15]; + ZVAL_COPY_VALUE(&args[0], values[0].raw()); + ZVAL_COPY_VALUE(&args[1], type.raw()); + ZVAL_COPY_VALUE(&args[2], values[1].raw()); + ZVAL_COPY_VALUE(&args[3], values[2].raw()); + ZVAL_COPY_VALUE(&args[4], values[3].raw()); + ZVAL_COPY_VALUE(&args[5], values[4].raw()); + ZVAL_COPY_VALUE(&args[6], values[5].raw()); + ZVAL_COPY_VALUE(&args[7], type.raw()); + for (int k = 6; k < 13; k++) { + ZVAL_COPY_VALUE(&args[k + 2], values[k].raw()); + } + return pt_extended_dummy_parameter_new(15, args); +} + +/* }}} */ + } // namespace namespace phpstanturbo { @@ -2543,9 +2739,28 @@ class ArgumentsHandler return closureParameterResolverResolveCallableTypeForScope(slot(slots::closureParameterResolver), closureExpr, scope); } + zv::Val siblingTemplateTypeMap; + zv::Val pushedParameterHold; + zval *pushedParameter = rawParameter; + zv::Val rawParameterType = pt_parameter_reflection_call(rawParameter, PT_PR_GET_TYPE); + if (UNEXPECTED(rawParameterType.isUndef())) return zv::Val(); + if (UNEXPECTED(!rawParameterType.ref().isObject())) { + zend_type_error("phpstan_turbo: getType() must return an object"); + return zv::Val(); + } + zv::Val receivedTemplateNames = getReceivedTemplateNames(rawParameterType.raw(), scope); + if (UNEXPECTED(receivedTemplateNames.isUndef())) return zv::Val(); + if (zend_hash_num_elements(Z_ARRVAL_P(receivedTemplateNames.raw())) > 0) { + siblingTemplateTypeMap = siblingTemplateTypeMapOf(w, i); + if (UNEXPECTED(siblingTemplateTypeMap.isUndef())) return zv::Val(); + pushedParameterHold = resolveReceivedTemplateTypes(rawParameter, receivedTemplateNames.raw(), siblingTemplateTypeMap.raw()); + if (UNEXPECTED(pushedParameterHold.isUndef())) return zv::Val(); + pushedParameter = pushedParameterHold.raw(); + } + zval null; ZVAL_NULL(&null); - zv::Val pushedScope = pt_mutating_scope_push_in_function_call(Z_OBJ_P(scope), &null, rawParameter, false); + zv::Val pushedScope = pt_mutating_scope_push_in_function_call(Z_OBJ_P(scope), &null, pushedParameter, 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(); @@ -2563,12 +2778,20 @@ class ArgumentsHandler if (UNEXPECTED(!referencesCalleeTemplateTypes(closureType.raw(), calleeTemplateTypeMap.raw(), references))) return zv::Val(); if (!references) return closureType; + if (siblingTemplateTypeMap.isUndef()) { + siblingTemplateTypeMap = siblingTemplateTypeMapOf(w, i); + if (UNEXPECTED(siblingTemplateTypeMap.isUndef())) return zv::Val(); + } + + return resolveCalleeTemplateTypes(closureType.raw(), calleeTemplateTypeMap.raw(), siblingTemplateTypeMap.raw()); + } + + /* Mirrors selectSiblingTemplateTypeMap() */ + zv::Val siblingTemplateTypeMapOf(Walk &w, zend_ulong i) const + { 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()); + return pt_parameters_acceptor_call(siblingAcceptor.raw(), PT_PA_GET_RESOLVED_TEMPLATE_TYPE_MAP); } /* the closure-type gather ahead of a closure / arrow function argument's diff --git a/turbo-ext/src/generated/ArgumentsHandler.h b/turbo-ext/src/generated/ArgumentsHandler.h index b9e44b6aaa3..192974a736c 100644 --- a/turbo-ext/src/generated/ArgumentsHandler.h +++ b/turbo-ext/src/generated/ArgumentsHandler.h @@ -120,45 +120,50 @@ inline constexpr char strings[] = "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 */ + "PHPStan\\Type\\Generic\\TemplateTypeMap\0" /* 1502 */ + "selectSiblingTemplateTypeMap\0" /* 1539 */ + "parameterType\0" /* 1568 */ + "getReceivedTemplateNames\0" /* 1582 */ + "parameter\0" /* 1607 */ + "PHPStan\\Reflection\\ParameterReflection\0" /* 1617 */ + "receivedTemplateNames\0" /* 1656 */ + "siblingTemplateTypeMap\0" /* 1678 */ + "resolveReceivedTemplateTypes\0" /* 1701 */ + "calleeTemplateTypeMap\0" /* 1730 */ + "referencesCalleeTemplateTypes\0" /* 1752 */ + "resolvedTemplateTypeMap\0" /* 1782 */ + "resolveCalleeTemplateTypes\0" /* 1806 */ + "PHPStan\\Type\\Generic\\TemplateType\0" /* 1833 */ + "isCalleeTemplateType\0" /* 1867 */ + "PHPStan\\Reflection\\ParametersAcceptor\0" /* 1888 */ + "selectCountStableMetadataAcceptor\0" /* 1926 */ + "acceptor\0" /* 1960 */ + "arg\0" /* 1969 */ + "matchArgParameter\0" /* 1973 */ + "selectArgsAcceptor\0" /* 1991 */ + "gatheredTypes\0" /* 2010 */ + "selectArgsMetadataAcceptor\0" /* 2024 */ + "originalCall\0" /* 2051 */ + "normalizedCall\0" /* 2064 */ + "processDroppedArgs\0" /* 2079 */ + "callCallbackImmediately\0" /* 2098 */ + "shouldInvalidateCallbackExpressions\0" /* 2122 */ + "getParameterTypeFromParameterClosureTypeExtension\0" /* 2158 */ + "currentParameter\0" /* 2208 */ + "getParameterOutExtensionsType\0" /* 2225 */ + "call\0" /* 2255 */ + "resolveClosureThisType\0" /* 2260 */ + "position\0" /* 2283 */ + "unpackedType\0" /* 2292 */ + "isPure\0" /* 2305 */ + "observeUnpackedArgument\0" /* 2312 */ + "valueType\0" /* 2336 */ + "observeArgumentValue\0" /* 2346 */ + "findOriginalParameterType\0" /* 2367 */ + "argResults\0" /* 2393 */ + "argValue\0" /* 2404 */ + "PHPStan\\Analyser\\ExpressionResult\0" /* 2413 */ + "readArgResult"; /* 2447 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 25), /* __construct $templateArgumentObserver */ reg::packed(76, 0, 100), /* __construct $expressionResultFactory */ @@ -207,88 +212,103 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1469, 0, 1388), /* gatherClosureArgType $closureExpr */ reg::packed(1074, 0, 1080), /* gatherClosureArgType $scope */ reg::packed(1245, 0, 1345), /* gatherClosureArgType return */ + reg::packed(832, 0, 850), /* selectSiblingTemplateTypeMap $nodeScopeResolver */ + reg::packed(1441, MAY_BE_ARRAY), /* selectSiblingTemplateTypeMap $args */ + reg::packed(993, MAY_BE_ARRAY), /* selectSiblingTemplateTypeMap $parametersAcceptors */ + reg::packed(1013, MAY_BE_NULL | MAY_BE_ARRAY), /* selectSiblingTemplateTypeMap $namedArgumentsVariants */ + reg::packed(1446, MAY_BE_ARRAY), /* selectSiblingTemplateTypeMap $gatheredArgTypeByIndex */ + reg::packed(1338, MAY_BE_LONG), /* selectSiblingTemplateTypeMap $i */ + reg::packed(1074, 0, 1080), /* selectSiblingTemplateTypeMap $scope */ + reg::packed(1245, 0, 1502), /* selectSiblingTemplateTypeMap return */ + reg::packed(1568, 0, 1345), /* getReceivedTemplateNames $parameterType */ + reg::packed(1074, 0, 1080), /* getReceivedTemplateNames $scope */ + reg::packed(1245, MAY_BE_ARRAY), /* getReceivedTemplateNames return */ + reg::packed(1607, 0, 1617), /* resolveReceivedTemplateTypes $parameter */ + reg::packed(1656, MAY_BE_ARRAY), /* resolveReceivedTemplateTypes $receivedTemplateNames */ + reg::packed(1678, 0, 1502), /* resolveReceivedTemplateTypes $siblingTemplateTypeMap */ + reg::packed(1245, 0, 1617), /* resolveReceivedTemplateTypes return */ reg::packed(1340, 0, 1345), /* referencesCalleeTemplateTypes $type */ - reg::packed(1502, 0, 1524), /* referencesCalleeTemplateTypes $calleeTemplateTypeMap */ + reg::packed(1730, 0, 1502), /* 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(1730, 0, 1502), /* resolveCalleeTemplateTypes $calleeTemplateTypeMap */ + reg::packed(1782, 0, 1502), /* 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(1340, 0, 1833), /* isCalleeTemplateType $type */ + reg::packed(1730, 0, 1502), /* isCalleeTemplateType $calleeTemplateTypeMap */ reg::packed(1245, MAY_BE_BOOL), /* isCalleeTemplateType return */ reg::packed(832, 0, 850), /* selectCountStableMetadataAcceptor $nodeScopeResolver */ 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, 1697), /* selectCountStableMetadataAcceptor return */ - reg::packed(1769, 0, 1697), /* matchArgParameter $acceptor */ - reg::packed(1778, 0, 1319), /* matchArgParameter $arg */ + reg::packed(1245, 0, 1888), /* selectCountStableMetadataAcceptor return */ + reg::packed(1960, 0, 1888), /* matchArgParameter $acceptor */ + reg::packed(1969, 0, 1319), /* matchArgParameter $arg */ reg::packed(1338, MAY_BE_LONG), /* matchArgParameter $i */ - reg::packed(1245, MAY_BE_NULL, 1782), /* matchArgParameter return */ + reg::packed(1245, MAY_BE_NULL, 1617), /* 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, 1697), /* selectArgsAcceptor return */ + reg::packed(1245, 0, 1888), /* selectArgsAcceptor return */ reg::packed(832, 0, 850), /* selectArgsMetadataAcceptor $nodeScopeResolver */ reg::packed(1441, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $args */ - reg::packed(1858, MAY_BE_ARRAY), /* selectArgsMetadataAcceptor $gatheredTypes */ + reg::packed(2010, 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, 1697), /* selectArgsMetadataAcceptor return */ + reg::packed(1245, 0, 1888), /* selectArgsMetadataAcceptor return */ reg::packed(832, 0, 850), /* processDroppedArgs $nodeScopeResolver */ reg::packed(885, 0, 890), /* processDroppedArgs $stmt */ - reg::packed(1899, 0, 1045), /* processDroppedArgs $originalCall */ - reg::packed(1912, 0, 1045), /* processDroppedArgs $normalizedCall */ + reg::packed(2051, 0, 1045), /* processDroppedArgs $originalCall */ + reg::packed(2064, 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(1946, MAY_BE_NULL, 1782), /* callCallbackImmediately $parameter */ - reg::packed(1956, MAY_BE_NULL, 1345), /* callCallbackImmediately $parameterType */ + reg::packed(1607, MAY_BE_NULL, 1617), /* callCallbackImmediately $parameter */ + reg::packed(1568, MAY_BE_NULL, 1345), /* callCallbackImmediately $parameterType */ reg::packed(910, 0), /* callCallbackImmediately $calleeReflection */ reg::packed(1245, MAY_BE_BOOL), /* callCallbackImmediately return */ - reg::packed(1946, MAY_BE_NULL, 1782), /* shouldInvalidateCallbackExpressions $parameter */ + reg::packed(1607, MAY_BE_NULL, 1617), /* 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(1946, 0, 1782), /* getParameterTypeFromParameterClosureTypeExtension $parameter */ + reg::packed(1607, 0, 1617), /* 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(2080, 0, 1782), /* getParameterOutExtensionsType $currentParameter */ + reg::packed(2208, 0, 1617), /* getParameterOutExtensionsType $currentParameter */ reg::packed(1074, 0, 1080), /* getParameterOutExtensionsType $scope */ reg::packed(1245, MAY_BE_NULL, 1345), /* getParameterOutExtensionsType return */ - reg::packed(2127, MAY_BE_NULL, 1045), /* resolveClosureThisType $call */ + reg::packed(2255, MAY_BE_NULL, 1045), /* resolveClosureThisType $call */ reg::packed(910, 0), /* resolveClosureThisType $calleeReflection */ - reg::packed(1946, 0, 1782), /* resolveClosureThisType $parameter */ + reg::packed(1607, 0, 1617), /* 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(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(1960, 0, 1888), /* observeUnpackedArgument $acceptor */ + reg::packed(2283, MAY_BE_LONG), /* observeUnpackedArgument $position */ + reg::packed(2292, 0, 1345), /* observeUnpackedArgument $unpackedType */ + reg::packed(2305, MAY_BE_BOOL), /* observeUnpackedArgument $isPure */ reg::packed(1245, 0, 1080), /* observeUnpackedArgument return */ reg::packed(1074, 0, 1080), /* observeArgumentValue $scope */ - 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(1960, 0, 1888), /* observeArgumentValue $acceptor */ + reg::packed(1607, 0, 1617), /* observeArgumentValue $parameter */ + reg::packed(2336, 0, 1345), /* observeArgumentValue $valueType */ + reg::packed(2305, MAY_BE_BOOL), /* observeArgumentValue $isPure */ reg::packed(1245, 0, 1080), /* observeArgumentValue return */ - reg::packed(1769, 0, 1697), /* findOriginalParameterType $acceptor */ - reg::packed(1946, 0, 1782), /* findOriginalParameterType $parameter */ + reg::packed(1960, 0, 1888), /* findOriginalParameterType $acceptor */ + reg::packed(1607, 0, 1617), /* findOriginalParameterType $parameter */ reg::packed(1245, MAY_BE_NULL, 1345), /* findOriginalParameterType return */ - reg::packed(2265, MAY_BE_ARRAY), /* readArgResult $argResults */ - reg::packed(2276, 0, 1388), /* readArgResult $argValue */ - reg::packed(1245, 0, 2285), /* readArgResult return */ + reg::packed(2393, MAY_BE_ARRAY), /* readArgResult $argResults */ + reg::packed(2404, 0, 1388), /* readArgResult $argValue */ + reg::packed(1245, 0, 2413), /* readArgResult return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -300,23 +320,26 @@ inline constexpr sigtab::Sig processArgs = { { 1274 /* processArgs */, 11, 16, 1 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 = { { 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 } }; +inline constexpr sigtab::Sig selectSiblingTemplateTypeMap = { { 1539 /* selectSiblingTemplateTypeMap */, 7, 47, 7, 54, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getReceivedTemplateNames = { { 1582 /* getReceivedTemplateNames */, 2, 55, 2, 57, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig resolveReceivedTemplateTypes = { { 1701 /* resolveReceivedTemplateTypes */, 3, 58, 3, 61, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig referencesCalleeTemplateTypes = { { 1752 /* referencesCalleeTemplateTypes */, 2, 62, 2, 64, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig resolveCalleeTemplateTypes = { { 1806 /* resolveCalleeTemplateTypes */, 3, 65, 3, 68, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig isCalleeTemplateType = { { 1867 /* isCalleeTemplateType */, 2, 69, 2, 71, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig selectCountStableMetadataAcceptor = { { 1926 /* selectCountStableMetadataAcceptor */, 5, 72, 5, 77, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig matchArgParameter = { { 1973 /* matchArgParameter */, 3, 78, 3, 81, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig selectArgsAcceptor = { { 1991 /* selectArgsAcceptor */, 5, 82, 5, 87, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig selectArgsMetadataAcceptor = { { 2024 /* selectArgsMetadataAcceptor */, 8, 88, 8, 96, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processDroppedArgs = { { 2079 /* processDroppedArgs */, 7, 97, 7, 104, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callCallbackImmediately = { { 2098 /* callCallbackImmediately */, 3, 105, 3, 108, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig shouldInvalidateCallbackExpressions = { { 2122 /* shouldInvalidateCallbackExpressions */, 1, 109, 1, 110, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getParameterTypeFromParameterClosureTypeExtension = { { 2158 /* getParameterTypeFromParameterClosureTypeExtension */, 4, 111, 4, 115, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getParameterOutExtensionsType = { { 2225 /* getParameterOutExtensionsType */, 4, 116, 4, 120, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig resolveClosureThisType = { { 2260 /* resolveClosureThisType */, 4, 121, 4, 125, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeUnpackedArgument = { { 2312 /* observeUnpackedArgument */, 5, 126, 5, 131, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeArgumentValue = { { 2346 /* observeArgumentValue */, 5, 132, 5, 137, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig findOriginalParameterType = { { 2367 /* findOriginalParameterType */, 2, 138, 2, 140, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig readArgResult = { { 2447 /* readArgResult */, 2, 141, 2, 143, ZEND_ACC_PRIVATE } }; } // namespace sig } // namespace ptdecl::ArgumentsHandler