From c29b7002b5386f7ecfb9985f403dfb5309694426 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:10 +0200 Subject: [PATCH 1/8] Re-walk only changed statements in later foreach convergence passes Each convergence pass after the first compares every statement's entry state with the previous pass's. A statement that mentions no variable whose state differs has its recorded emissions replayed and its recorded effect carried onto the current scope; once the entry state equals the previous pass's, the rest of that pass stands. Variables holding a closure that captures a differing variable by reference count as mentions of it, since invoking the closure reads the variable. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- src/Analyser/InternalStatementExitPoint.php | 5 + src/Analyser/InternalThrowPoint.php | 5 + src/Analyser/NodeScopeResolver.php | 33 ++ src/Analyser/StatementsHandler.php | 139 +++++++ src/Analyser/StmtHandler/ForeachHandler.php | 13 +- turbo-ext/src/Abi.cpp | 6 + turbo-ext/src/ForeachHandler.cpp | 11 +- turbo-ext/src/InternalStatementExitPoint.cpp | 10 + turbo-ext/src/InternalThrowPoint.cpp | 13 + turbo-ext/src/NodeScopeResolver.cpp | 58 +++ turbo-ext/src/StatementsHandler.cpp | 257 ++++++++++++ turbo-ext/src/abi.h | 4 + .../generated/InternalStatementExitPoint.h | 9 +- turbo-ext/src/generated/InternalThrowPoint.h | 30 +- turbo-ext/src/generated/NodeScopeResolver.h | 299 +++++++------- turbo-ext/src/generated/StatementsHandler.h | 380 ++++++++++-------- turbo-ext/src/support.h | 7 + 17 files changed, 943 insertions(+), 336 deletions(-) diff --git a/src/Analyser/InternalStatementExitPoint.php b/src/Analyser/InternalStatementExitPoint.php index 568e4439b5d..9b7f979d78f 100644 --- a/src/Analyser/InternalStatementExitPoint.php +++ b/src/Analyser/InternalStatementExitPoint.php @@ -23,6 +23,11 @@ public function getStatement(): Stmt return $this->statement; } + public function withScope(MutatingScope $scope): self + { + return new self($this->statement, $scope); + } + public function getScope(): MutatingScope { return $this->scope; diff --git a/src/Analyser/InternalThrowPoint.php b/src/Analyser/InternalThrowPoint.php index 1f7a249c986..17d03489df3 100644 --- a/src/Analyser/InternalThrowPoint.php +++ b/src/Analyser/InternalThrowPoint.php @@ -56,6 +56,11 @@ public static function createFromPublic(ThrowPoint $throwPoint, MutatingScope $s return new self($scope, $throwPoint->getType(), $throwPoint->getNode(), $throwPoint->isExplicit(), $throwPoint->canContainAnyThrowable()); } + public function withScope(MutatingScope $scope): self + { + return new self($scope, $this->type, $this->node, $this->explicit, $this->canContainAnyThrowable); + } + public function getScope(): MutatingScope { return $this->scope; diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 80269413519..b2dfcb07d4c 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -286,6 +286,39 @@ public function processStmtNodes( } } + /** + * One loop convergence pass over $stmts, re-walking only what differs from + * $previous, the previous pass (see StatementsHandler::processLoopPassStmtNodes()). + * + * @param Node\Stmt[] $stmts + * @param callable(Node $node, Scope $scope): void $nodeCallback + * @param array{array, ?RecordingNodeCallback}|null $previous + * @param array{array, ?RecordingNodeCallback}|null $record + */ + public function processLoopPassStmtNodes( + Node $parentNode, + array $stmts, + MutatingScope $scope, + ExpressionResultStorage $storage, + callable $nodeCallback, + StatementContext $context, + ?array $previous, + ?array &$record, + ): InternalStatementResult + { + $pushStorage = $scope->getCurrentExpressionResultStorage() !== $storage; + if ($pushStorage) { + $scope->pushExpressionResultStorage($storage); + } + try { + return $this->statementsHandler->processLoopPassStmtNodes($this, $parentNode, $stmts, $scope, $storage, $nodeCallback, $context, $previous, $record); + } finally { + if ($pushStorage) { + $scope->popExpressionResultStorage(); + } + } + } + /** * @param Node\Stmt[] $stmts * @param callable(Node $node, Scope $scope): void $nodeCallback diff --git a/src/Analyser/StatementsHandler.php b/src/Analyser/StatementsHandler.php index 2dc24588f17..9140085de4f 100644 --- a/src/Analyser/StatementsHandler.php +++ b/src/Analyser/StatementsHandler.php @@ -31,6 +31,7 @@ use PHPStan\PhpDoc\Tag\VarTag; use PHPStan\TrinaryLogic; use PHPStan\Turbo\ShadowedByTurboExtension; +use PHPStan\Type\ClosureType; use PHPStan\Type\ErrorType; use PHPStan\Type\FileTypeMapper; use PHPStan\Type\MixedType; @@ -38,8 +39,10 @@ use PHPStan\Type\NullType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\UnionType; use function array_column; use function array_fill_keys; +use function array_flip; use function array_key_exists; use function array_keys; use function array_merge; @@ -397,6 +400,142 @@ public function doProcessStmtNodes( return $statementResult; } + /** + * One loop convergence pass. Against the previous pass's per-statement + * entries, a statement is re-walked only when its entry state differs in a + * key spelling no variable, or it mentions a variable whose state differs; + * otherwise its recorded emissions are replayed and its recorded effect is + * carried onto the current scope. Once the entry state equals the previous + * pass's, the rest of the previous pass stands. $record receives this pass's + * entries for the next one. + * + * @param Node\Stmt[] $stmts + * @param callable(Node $node, Scope $scope): void $nodeCallback + * @param array{array, ?RecordingNodeCallback}|null $previous + * @param array{array, ?RecordingNodeCallback}|null $record + * @param-out array{array, ?RecordingNodeCallback} $record + */ + public function processLoopPassStmtNodes( + NodeScopeResolver $nodeScopeResolver, + Node $parentNode, + array $stmts, + MutatingScope $scope, + ExpressionResultStorage $storage, + callable $nodeCallback, + StatementContext $context, + ?array $previous, + ?array &$record, + ): InternalStatementResult + { + $recording = $nodeCallback instanceof RecordingNodeCallback ? $nodeCallback : null; + $previousRecording = $previous !== null ? $previous[1] : null; + // a replayed statement's emissions come from the previous recording; + // labels make the statement order no straight line + $canReplay = $previous !== null + && ($recording === null || $previousRecording !== null) + && !$this->containsLabels($stmts); + $stmtCount = count($stmts); + $state = new StatementListWalkState($scope); + $entries = []; + for ($i = 0; $i < $stmtCount; $i++) { + $entries[$i] = [clone $state, $recording !== null ? $recording->count() : 0]; + if ($canReplay) { + [$recordedEntry, $offset] = $previous[0][$i]; + [$recordedExit, $nextOffset] = $previous[0][$i + 1]; + $differingRoots = $state->alreadyTerminated === $recordedEntry->alreadyTerminated + ? $state->scope->getDifferingVariableRoots($recordedEntry->scope) + : null; + if ($differingRoots === []) { + // converged with the previous pass: the rest of it stands + [$recordedEnd, $endOffset] = $previous[0][$stmtCount]; + if ($recording !== null && $previousRecording !== null) { + $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $endOffset, $recording, $storage, $scope); + } + $this->appendRecordedStatementResults($state, $recordedEntry, $recordedEnd); + $state->scope = $recordedEnd->scope; + $base = $entries[$i][1]; + for ($j = $i; $j <= $stmtCount; $j++) { + $entries[$j] = [$previous[0][$j][0], $base + $previous[0][$j][1] - $offset]; + } + $record = [$entries, $recording]; + + return $state->toResult(); + } + if ($differingRoots !== null && !$this->statementMentionsAnyVariable($stmts[$i], $this->withByRefClosureHolders($state->scope, $differingRoots))) { + if ($recording !== null && $previousRecording !== null) { + $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $nextOffset, $recording, $storage, $scope); + } + $exitPointCount = count($state->exitPoints); + $throwPointCount = count($state->throwPoints); + $this->appendRecordedStatementResults($state, $recordedEntry, $recordedExit); + $this->rebaseAppendedPoints($state, $exitPointCount, $throwPointCount, $recordedEntry->scope); + $state->scope = $state->scope->withRecordedStatementDelta($recordedEntry->scope, $recordedExit->scope); + continue; + } + } + $this->processStatementStep($nodeScopeResolver, $parentNode, $stmts, $i, $stmts[$i], $state, $storage, $nodeCallback, $context, false); + } + $entries[$stmtCount] = [clone $state, $recording !== null ? $recording->count() : 0]; + $record = [$entries, $recording]; + + return $state->toResult(); + } + + /** + * $variableNames and the variables holding a closure that captures one of + * them by reference: invoking it walks its body with the variable's current + * state (ClosureSignatureInference), so `$c();` reads `$x` without spelling it. + * + * @param list $variableNames + * @return list + */ + private function withByRefClosureHolders(MutatingScope $scope, array $variableNames): array + { + if ($variableNames === []) { + return $variableNames; + } + $differing = array_flip($variableNames); + foreach ([...$scope->getDefinedVariables(), ...$scope->getMaybeDefinedVariables()] as $holder) { + if (isset($differing[$holder])) { + continue; + } + $type = $scope->getVariableType($holder); + foreach ($type instanceof UnionType ? $type->getTypes() : [$type] as $member) { + if (!$member instanceof ClosureType) { + continue; + } + foreach (array_keys($member->getByRefUseTypes()) as $captured) { + if (!isset($differing[$captured])) { + continue; + } + + $variableNames[] = $holder; + continue 3; + } + } + } + + return $variableNames; + } + + /** + * The exit and throw points a replayed statement appended carry scopes of + * the recorded walk; carry the statement's recorded effect onto the current + * entry scope instead. + */ + private function rebaseAppendedPoints(StatementListWalkState $state, int $exitPointCount, int $throwPointCount, MutatingScope $recordedEntry): void + { + $entry = $state->scope; + for ($i = $exitPointCount; $i < count($state->exitPoints); $i++) { + $point = $state->exitPoints[$i]; + $state->exitPoints[$i] = $point->withScope($entry->withRecordedStatementDelta($recordedEntry, $point->getScope())); + } + for ($i = $throwPointCount; $i < count($state->throwPoints); $i++) { + $point = $state->throwPoints[$i]; + $state->throwPoints[$i] = $point->withScope($entry->withRecordedStatementDelta($recordedEntry, $point->getScope())); + } + } + /** * One statement of a statement list, advancing $state past it. * diff --git a/src/Analyser/StmtHandler/ForeachHandler.php b/src/Analyser/StmtHandler/ForeachHandler.php index dea3ecfb40b..0c264a91360 100644 --- a/src/Analyser/StmtHandler/ForeachHandler.php +++ b/src/Analyser/StmtHandler/ForeachHandler.php @@ -226,6 +226,10 @@ static function () use ($condResult, $emptyArrayType): Type { } $count = 0; $prevEntryScope = null; + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + $previousPass = null; + $previousPassStorage = null; $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); do { $prevScope = $bodyScope; @@ -238,11 +242,18 @@ static function () use ($condResult, $emptyArrayType): Type { } $prevEntryScope = $bodyScope; $storage = $originalStorage->duplicate(); + if ($previousPassStorage !== null) { + // the statements this pass replays answer from the results the previous pass stored + $storage->mergeResults($previousPassStorage); + } $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); $scope->pushExpressionResultStorage($storage); try { $bodyScope = $this->enterForeach($nodeScopeResolver, $bodyScope, $storage, $originalScope, $stmt, $foreachIterateeType, $foreachNativeIterateeType, $nodeCallback); - $bodyScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution())->filterOutLoopExitPoints(); + $passRecord = null; + $bodyScopeResult = $nodeScopeResolver->processLoopPassStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution(), $previousPass, $passRecord)->filterOutLoopExitPoints(); + $previousPass = $passRecord; + $previousPassStorage = $storage; $backEdgeScope = $bodyScopeResult->getLoopBackEdgeScope(); if ($backEdgeScope === null) { $bodyScope = $prevScope; diff --git a/turbo-ext/src/Abi.cpp b/turbo-ext/src/Abi.cpp index e49d77d0d60..2e3ce0d961e 100644 --- a/turbo-ext/src/Abi.cpp +++ b/turbo-ext/src/Abi.cpp @@ -119,6 +119,11 @@ static void abi_try_assign_ref_true(zval *zv) ZEND_TRY_ASSIGN_REF_TRUE(zv); } +static void abi_try_assign_ref_copy(zval *zv, zval *value) +{ + ZEND_TRY_ASSIGN_REF_COPY(zv, value); +} + /* the neutral ZPP numbering of abi.h, translated for this engine */ static const int abi_zpp_errors[] = { ZPP_ERROR_OK, @@ -364,6 +369,7 @@ void pt_abi_init() pt_abi.call_stack_size_error = abi_call_stack_size_error; pt_abi.try_assign_ref_str = abi_try_assign_ref_str; pt_abi.try_assign_ref_true = abi_try_assign_ref_true; + pt_abi.try_assign_ref_copy = abi_try_assign_ref_copy; pt_abi.wrong_parameter_error = abi_wrong_parameter_error; pt_abi.parse_arg_bool_slow = abi_parse_arg_bool_slow; pt_abi.parse_arg_double_slow = abi_parse_arg_double_slow; diff --git a/turbo-ext/src/ForeachHandler.cpp b/turbo-ext/src/ForeachHandler.cpp index 49ca2f9827f..8debfcbb8d2 100644 --- a/turbo-ext/src/ForeachHandler.cpp +++ b/turbo-ext/src/ForeachHandler.cpp @@ -487,6 +487,10 @@ class ForeachHandler if (UNEXPECTED(EG(exception))) return zv::Val(); zend_long count = 0; zv::Val prevEntryScope; + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); bool bodyIsReplayable; if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, parts.stmts.raw(), bodyIsReplayable))) return zv::Val(); do { @@ -506,6 +510,8 @@ class ForeachHandler prevEntryScope = zv::Val::copyOf(bodyScope.ref()); storage = pt_expression_result_storage_duplicate(originalStorage); if (UNEXPECTED(storage.isUndef())) return zv::Val(); + // the statements this pass replays answer from the results the previous pass stored + if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); if (UNEXPECTED(bodyRecording.isUndef())) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope.raw()), storage.raw()))) return zv::Val(); @@ -519,8 +525,11 @@ class ForeachHandler if (UNEXPECTED(deepContext.isUndef())) return; zv::Val passContext = pt_statement_context_without_template_argument_resolution(deepContext.raw()); if (UNEXPECTED(passContext.isUndef())) return; - zv::Val walked = pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, parts.stmts.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw()); + zv::Val passRecord = zv::Val::null(); + zv::Val walked = pt_node_scope_resolver_process_loop_pass_stmt_nodes(nodeScopeResolver, stmt, parts.stmts.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw(), previousPass.raw(), passRecord); if (UNEXPECTED(walked.isUndef())) return; + previousPass = std::move(passRecord); + previousPassStorage = zv::Val::copyOf(storage.ref()); bodyScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); if (UNEXPECTED(bodyScopeResult.isUndef())) return; zv::Val backEdgeScope = pt_internal_statement_result_loop_back_edge_scope(bodyScopeResult.raw()); diff --git a/turbo-ext/src/InternalStatementExitPoint.cpp b/turbo-ext/src/InternalStatementExitPoint.cpp index e34d2647cf2..93e219e20a1 100644 --- a/turbo-ext/src/InternalStatementExitPoint.cpp +++ b/turbo-ext/src/InternalStatementExitPoint.cpp @@ -52,6 +52,14 @@ class InternalStatementExitPoint return pt_statement_exit_point_new(statement, scope); } + /* Mirrors withScope(): new self($this->statement, $scope) */ + zv::Val withScope(zval *scope) const + { + zval *statement = pt_typed_slot(self, slots::statement, self->ce, "statement"); + if (UNEXPECTED(statement == NULL)) return zv::Val(); + return create(statement, scope); + } + zv::Val getStatement() const { return read(slots::statement, "statement"); } zv::Val getScope() const { return read(slots::scope, "scope"); } @@ -107,6 +115,8 @@ PT_MINIT_REGISTRATION(pt_register_internal_statement_exit_point) cls.method<&InternalStatementExitPoint::getStatement>(sigs::getStatement); + cls.method<&InternalStatementExitPoint::withScope, zp::Obj>(sigs::withScope); + cls.method<&InternalStatementExitPoint::getScope>(sigs::getScope); cls.shadow(&pt_ce_internal_statement_exit_point); diff --git a/turbo-ext/src/InternalThrowPoint.cpp b/turbo-ext/src/InternalThrowPoint.cpp index e4671cc4db1..d96e8c789ee 100644 --- a/turbo-ext/src/InternalThrowPoint.cpp +++ b/turbo-ext/src/InternalThrowPoint.cpp @@ -114,6 +114,17 @@ class InternalThrowPoint return newSelf(scope, type.raw(), node.raw(), zend_is_true(explicit_.raw()), zend_is_true(canContainAnyThrowable.raw())); } + /* Mirrors withScope(). */ + zv::Val withScope(zval *scope) const + { + zval *type = pt_typed_slot(self, slots::type, self->ce, "type"); + zval *node = type != NULL ? pt_typed_slot(self, slots::node, self->ce, "node") : NULL; + zval *explicit_ = node != NULL ? pt_typed_slot(self, slots::explicit_, self->ce, "explicit") : NULL; + zval *canContainAnyThrowable = explicit_ != NULL ? pt_typed_slot(self, slots::canContainAnyThrowable, self->ce, "canContainAnyThrowable") : NULL; + if (UNEXPECTED(canContainAnyThrowable == NULL)) return zv::Val(); + return newSelf(scope, type, node, Z_TYPE_P(explicit_) == IS_TRUE, Z_TYPE_P(canContainAnyThrowable) == IS_TRUE); + } + zv::Val getScope() const { return read(slots::scope, "scope"); } zv::Val getType() const { return read(slots::type, "type"); } zv::Val getNode() const { return read(slots::node, "node"); } @@ -214,6 +225,8 @@ PT_MINIT_REGISTRATION(pt_register_internal_throw_point) cls.method<&InternalThrowPoint::createFromPublic, zp::Obj, zp::Obj>(sigs::createFromPublic); + cls.method<&InternalThrowPoint::withScope, zp::Obj>(sigs::withScope); + cls.method<&InternalThrowPoint::getScope>(sigs::getScope); cls.method<&InternalThrowPoint::getType>(sigs::getType); diff --git a/turbo-ext/src/NodeScopeResolver.cpp b/turbo-ext/src/NodeScopeResolver.cpp index 4c78ea9b6ba..2ff537ed988 100644 --- a/turbo-ext/src/NodeScopeResolver.cpp +++ b/turbo-ext/src/NodeScopeResolver.cpp @@ -645,6 +645,30 @@ class NodeScopeResolver return result; } + /* Mirrors processLoopPassStmtNodes(); previous IS_NULL for the first + * pass, record receives this pass's [entries, recording] */ + zv::Val processLoopPassStmtNodes(zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) + { + zv::Val result; + pt_engine_with_stack([&]() { + zv::Val current = pt_mutating_scope_get_current_expression_result_storage(Z_OBJ_P(scope)); + if (UNEXPECTED(current.isUndef())) return; + bool pushStorage = !(current.ref().isObject() && Z_OBJ_P(current.raw()) == Z_OBJ_P(storage)); + if (pushStorage && UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope), storage))) return; + zval thisZv; + ZVAL_OBJ(&thisZv, self); + zval *statementsHandler = requireStatementsHandler(); + if (EXPECTED(statementsHandler != NULL)) { + result = pt_statements_handler_process_loop_pass_stmt_nodes(statementsHandler, &thisZv, parentNode, stmts, scope, storage, nodeCallback, context, previous, record); + } + if (pushStorage) { + pt_finally([&]() { (void) pt_mutating_scope_pop_expression_result_storage(Z_OBJ_P(scope)); }); + if (UNEXPECTED(EG(exception))) result = zv::Val(); + } + }); + return result; + } + /* Mirrors processStmtNode(). */ zv::Val processStmtNode(zval *stmt, zval *scope, zval *storage, zval *nodeCallback, zval *context) { @@ -2128,6 +2152,21 @@ zv::Val pt_node_scope_resolver_process_stmt_nodes_internal(zval *nodeScopeResolv return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).thisProcessStmtNodesInternal(parentNode, stmts, scope, storage, nodeCallback, context); } +zv::Val pt_node_scope_resolver_process_loop_pass_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) +{ + if (isNativeResolver(nodeScopeResolver)) return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).processLoopPassStmtNodes(parentNode, stmts, scope, storage, nodeCallback, context, previous, record); + zval recordRef; + ZVAL_NULL(&recordRef); + ZVAL_MAKE_REF(&recordRef); + zval null; + ZVAL_NULL(&null); + zv::Args argv{parentNode, stmts, scope, storage, nodeCallback, context, previous != NULL ? previous : &null, &recordRef}; + zv::Val result = pt_type_call(Z_OBJ_P(nodeScopeResolver), PT_LC("processlooppassstmtnodes"), 8, argv); + record = zv::Val::copyOf(zv::Ref(Z_REFVAL(recordRef))); + zval_ptr_dtor(&recordRef); + return result; +} + zv::Val pt_node_scope_resolver_process_expr_on_demand(zval *nodeScopeResolver, zval *expr, zval *scope, zval *storage) { return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).thisProcessExprOnDemand(expr, scope, storage); @@ -2453,6 +2492,25 @@ PT_MINIT_REGISTRATION(pt_register_node_scope_resolver) PT_RETURN_VAL(PT_NSR_THIS.processStmtNodes(parentNode, stmts, scope, nodeCallback, context)); }); + cls.method(sigs::processLoopPassStmtNodes, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *parentNode, *stmts, *scope, *storage, *nodeCallback, *context, *previous, *recordRef; + ZEND_PARSE_PARAMETERS_START(8, 8) + Z_PARAM_OBJECT(parentNode) + Z_PARAM_ARRAY(stmts) + Z_PARAM_OBJECT(scope) + Z_PARAM_OBJECT(storage) + Z_PARAM_ZVAL(nodeCallback) + Z_PARAM_OBJECT(context) + Z_PARAM_ARRAY_OR_NULL(previous) + Z_PARAM_ZVAL(recordRef) + ZEND_PARSE_PARAMETERS_END(); + zv::Val record = zv::Val::null(); + zv::Val result = PT_NSR_THIS.processLoopPassStmtNodes(parentNode, stmts, scope, storage, nodeCallback, context, previous, record); + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); + ZEND_TRY_ASSIGN_REF_COPY(recordRef, record.raw()); + PT_RETURN_VAL(std::move(result)); + }); + cls.method(sigs::processStmtNodesInternal, [](INTERNAL_FUNCTION_PARAMETERS) { zval *parentNode, *stmts, *scope, *storage, *nodeCallback, *context; ZEND_PARSE_PARAMETERS_START(6, 6) diff --git a/turbo-ext/src/StatementsHandler.cpp b/turbo-ext/src/StatementsHandler.cpp index 348df2cb700..6937cfa113c 100644 --- a/turbo-ext/src/StatementsHandler.cpp +++ b/turbo-ext/src/StatementsHandler.cpp @@ -656,6 +656,228 @@ class StatementsHandler return statementResult; } + /* Mirrors processLoopPassStmtNodes(): one loop convergence pass that + * re-walks only the statements whose entry differs from the previous + * pass's; $previous IS_NULL for the first pass, record receives this + * pass's [entries, recording]. UNDEF = pending exception */ + zv::Val processLoopPassStmtNodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) + { + bool isRecording = Z_TYPE_P(nodeCallback) == IS_OBJECT && instanceof_function(Z_OBJCE_P(nodeCallback), pt_ce_recording_node_callback); + zval *recording = isRecording ? nodeCallback : NULL; + bool hasPrevious = previous != NULL && Z_TYPE_P(previous) == IS_ARRAY; + zval *previousEntries = hasPrevious ? zend_hash_index_find(Z_ARRVAL_P(previous), 0) : NULL; + zval *previousRecording = hasPrevious ? zend_hash_index_find(Z_ARRVAL_P(previous), 1) : NULL; + if (previousRecording != NULL && Z_TYPE_P(previousRecording) == IS_NULL) previousRecording = NULL; + // a replayed statement's emissions come from the previous recording; + // labels make the statement order no straight line + bool canReplay = hasPrevious && previousEntries != NULL && (recording == NULL || previousRecording != NULL); + zv::Val stmtsHeld = zv::Val::copyOf(zv::Ref(stmts)); + if (canReplay) { + bool hasLabels; + if (UNEXPECTED(!containsLabels(stmtsHeld.raw(), hasLabels))) return zv::Val(); + canReplay = !hasLabels; + } + HashTable *stmtsTable = Z_ARRVAL_P(stmtsHeld.raw()); + zend_long stmtCount = zend_hash_num_elements(stmtsTable); + zv::Val state = pt_statement_list_walk_state_new(scope); + if (UNEXPECTED(state.isUndef())) return zv::Val(); + zv::Arr entries = zv::Arr::create((uint32_t) stmtCount + 1); + for (zend_long i = 0; i < stmtCount; i++) { + zv::Val entry = snapshotPassEntry(state.raw(), recording); + if (UNEXPECTED(entry.isUndef())) return zv::Val(); + zend_long entryCount = zval_get_long(entryOffset(entry.raw())); + entries.push(std::move(entry)); + zval *stmt = zend_hash_index_find(stmtsTable, (zend_ulong) i); + if (UNEXPECTED(stmt == NULL)) { + zend_throw_error(NULL, "phpstan_turbo: the loop body's statements are not a list"); + return zv::Val(); + } + ZVAL_DEREF(stmt); + if (canReplay) { + zval *recordedEntryPair = entryAt(previousEntries, i); + zval *recordedExitPair = recordedEntryPair != NULL ? entryAt(previousEntries, i + 1) : NULL; + if (UNEXPECTED(recordedExitPair == NULL)) return zv::Val(); + zval *recordedEntry = entryState(recordedEntryPair); + zend_long offset = zval_get_long(entryOffset(recordedEntryPair)); + zval *recordedExit = entryState(recordedExitPair); + zend_long nextOffset = zval_get_long(entryOffset(recordedExitPair)); + zend_object *stateObject = Z_OBJ_P(state.raw()); + zend_object *recordedEntryObject = Z_OBJ_P(recordedEntry); + zv::Val differingRoots = zv::Val::null(); + bool stateTerminated = Z_TYPE_P(OBJ_PROP_NUM(stateObject, stateSlots::alreadyTerminated)) == IS_TRUE; + bool entryTerminated = Z_TYPE_P(OBJ_PROP_NUM(recordedEntryObject, stateSlots::alreadyTerminated)) == IS_TRUE; + if (stateTerminated == entryTerminated) { + differingRoots = pt_mutating_scope_get_differing_variable_roots(Z_OBJ_P(OBJ_PROP_NUM(stateObject, stateSlots::scope)), Z_OBJ_P(OBJ_PROP_NUM(recordedEntryObject, stateSlots::scope))); + if (UNEXPECTED(differingRoots.isUndef())) return zv::Val(); + } + if (differingRoots.ref().isArray() && zend_hash_num_elements(Z_ARRVAL_P(differingRoots.raw())) == 0) { + // converged with the previous pass: the rest of it stands + zval *recordedEndPair = entryAt(previousEntries, stmtCount); + if (UNEXPECTED(recordedEndPair == NULL)) return zv::Val(); + zval *recordedEnd = entryState(recordedEndPair); + zend_long endOffset = zval_get_long(entryOffset(recordedEndPair)); + if (recording != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, endOffset, recording, storage, scope))) return zv::Val(); + if (UNEXPECTED(!appendRecordedStatementResults(state.raw(), recordedEntry, recordedEnd))) return zv::Val(); + zv::Ref(OBJ_PROP_NUM(Z_OBJ_P(state.raw()), stateSlots::scope)).assign(zv::Val::copyOf(zv::Ref(OBJ_PROP_NUM(Z_OBJ_P(recordedEnd), stateSlots::scope)))); + // $entries[$j] = [$previous[0][$j][0], $base + $previous[0][$j][1] - $offset] for $j >= $i + zv::Arr adopted = zv::Arr::create((uint32_t) stmtCount + 1); + zend_long j = 0; + for (auto existing : zv::ArrRef(entries.raw())) { + if (j++ >= i) break; + adopted.push(zv::Val::copyOf(existing.value())); + } + for (j = i; j <= stmtCount; j++) { + zval *pair = entryAt(previousEntries, j); + if (UNEXPECTED(pair == NULL)) return zv::Val(); + zv::Arr shifted = zv::Arr::create(2); + shifted.push(zv::Val::copyOf(zv::Ref(entryState(pair)))); + shifted.push(zv::Val::integer(entryCount + zval_get_long(entryOffset(pair)) - offset)); + adopted.push(zv::Val(std::move(shifted))); + } + record = passRecord(zv::Val(std::move(adopted)), recording); + return pt_statement_list_walk_state_to_result(state.raw()); + } + bool reWalk = differingRoots.isNull(); + if (!reWalk) { + zv::Val mentioned = withByRefClosureHolders(OBJ_PROP_NUM(stateObject, stateSlots::scope), differingRoots.raw()); + if (UNEXPECTED(mentioned.isUndef())) return zv::Val(); + if (UNEXPECTED(!statementMentionsAnyVariable(stmt, mentioned.raw(), reWalk))) return zv::Val(); + } + if (!reWalk) { + if (recording != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, nextOffset, recording, storage, scope))) return zv::Val(); + zend_long exitPointCount = zend_hash_num_elements(Z_ARRVAL_P(OBJ_PROP_NUM(stateObject, stateSlots::exitPoints))); + zend_long throwPointCount = zend_hash_num_elements(Z_ARRVAL_P(OBJ_PROP_NUM(stateObject, stateSlots::throwPoints))); + if (UNEXPECTED(!appendRecordedStatementResults(state.raw(), recordedEntry, recordedExit))) return zv::Val(); + zval *recordedEntryScope = OBJ_PROP_NUM(recordedEntryObject, stateSlots::scope); + if (UNEXPECTED(!rebaseAppendedPoints(state.raw(), exitPointCount, throwPointCount, recordedEntryScope))) return zv::Val(); + zval *currentScope = OBJ_PROP_NUM(Z_OBJ_P(state.raw()), stateSlots::scope); + zv::Val delta = pt_mutating_scope_with_recorded_statement_delta(Z_OBJ_P(currentScope), Z_OBJ_P(recordedEntryScope), Z_OBJ_P(OBJ_PROP_NUM(Z_OBJ_P(recordedExit), stateSlots::scope))); + if (UNEXPECTED(delta.isUndef())) return zv::Val(); + zv::Ref(OBJ_PROP_NUM(Z_OBJ_P(state.raw()), stateSlots::scope)).assign(std::move(delta)); + continue; + } + } + if (UNEXPECTED(!processStatementStep(nodeScopeResolver, parentNode, stmts, i, stmt, state.raw(), storage, nodeCallback, context, false))) return zv::Val(); + } + zv::Val last = snapshotPassEntry(state.raw(), recording); + if (UNEXPECTED(last.isUndef())) return zv::Val(); + entries.push(std::move(last)); + record = passRecord(zv::Val(std::move(entries)), recording); + return pt_statement_list_walk_state_to_result(state.raw()); + } + + /* [clone $state, $recording !== null ? $recording->count() : 0] */ + static zv::Val snapshotPassEntry(zval *state, zval *recording) + { + if (recording != NULL) return snapshotEntry(state, recording); + zv::Val cloned = cloneObject(state); + if (UNEXPECTED(cloned.isUndef())) return zv::Val(); + zv::Arr pair = zv::Arr::create(2); + pair.push(std::move(cloned)); + pair.push(zv::Val::integer(0)); + return zv::Val(std::move(pair)); + } + + /* [$entries, $recording] */ + static zv::Val passRecord(zv::Val entries, zval *recording) + { + zv::Arr pair = zv::Arr::create(2); + pair.push(std::move(entries)); + if (recording != NULL) { + pair.push(zv::Val::copyOf(zv::Ref(recording))); + } else { + pair.push(zv::Val::null()); + } + return zv::Val(std::move(pair)); + } + + /* Mirrors the private rebaseAppendedPoints(); false = pending exception */ + [[nodiscard]] static bool rebaseAppendedPoints(zval *state, zend_long exitPointCount, zend_long throwPointCount, zval *recordedEntry) + { + zend_object *stateObject = Z_OBJ_P(state); + zv::Val entry = zv::Val::copyOf(zv::Ref(OBJ_PROP_NUM(stateObject, stateSlots::scope))); + for (uint32_t pointsSlot : { stateSlots::exitPoints, stateSlots::throwPoints }) { + zend_long from = pointsSlot == stateSlots::exitPoints ? exitPointCount : throwPointCount; + zval *points = OBJ_PROP_NUM(stateObject, pointsSlot); + zend_long count = zend_hash_num_elements(Z_ARRVAL_P(points)); + for (zend_long i = from; i < count; i++) { + zval *point = zend_hash_index_find(Z_ARRVAL_P(points), (zend_ulong) i); + if (UNEXPECTED(point == NULL)) { + zend_throw_error(NULL, "phpstan_turbo: the walk state's points are not a list"); + return false; + } + zv::Val pointScope = pt_type_call(Z_OBJ_P(point), PT_LC("getscope"), 0, NULL); + if (UNEXPECTED(pointScope.isUndef())) return false; + zv::Val rebasedScope = pt_mutating_scope_with_recorded_statement_delta(Z_OBJ_P(entry.raw()), Z_OBJ_P(recordedEntry), Z_OBJ_P(pointScope.raw())); + if (UNEXPECTED(rebasedScope.isUndef())) return false; + zv::Val rebased = pt_type_call(Z_OBJ_P(point), PT_LC("withscope"), 1, rebasedScope.raw()); + if (UNEXPECTED(rebased.isUndef())) return false; + points = OBJ_PROP_NUM(stateObject, pointsSlot); + SEPARATE_ARRAY(points); + zend_hash_index_update(Z_ARRVAL_P(points), (zend_ulong) i, rebased.raw()); + ZVAL_UNDEF(rebased.raw()); + } + } + return true; + } + + /* Mirrors the private withByRefClosureHolders(); UNDEF = pending exception */ + static zv::Val withByRefClosureHolders(zval *scope, zval *variableNames) + { + zv::Val names = zv::Val::copyOf(zv::Ref(variableNames)); + if (zend_hash_num_elements(Z_ARRVAL_P(names.raw())) == 0) return names; + HashTable differing; + zend_hash_init(&differing, 8, NULL, NULL, 0); + for (auto entry : zv::ArrRef(variableNames)) { + zval *name = entry.value().deref().raw(); + if (Z_TYPE_P(name) == IS_STRING) zend_hash_add_empty_element(&differing, Z_STR_P(name)); + } + bool ok = true; + for (int kind = 0; kind < 2 && ok; kind++) { + zv::Val defined = kind == 0 ? pt_mutating_scope_get_defined_variables(Z_OBJ_P(scope)) : pt_mutating_scope_get_maybe_defined_variables(Z_OBJ_P(scope)); + if (UNEXPECTED(defined.isUndef())) { ok = false; break; } + for (auto entry : zv::ArrRef(defined.raw())) { + zval *holder = entry.value().deref().raw(); + if (Z_TYPE_P(holder) != IS_STRING || zend_hash_exists(&differing, Z_STR_P(holder))) continue; + zv::Val type = pt_mutating_scope_get_variable_type(Z_OBJ_P(scope), Z_STR_P(holder)); + if (UNEXPECTED(type.isUndef())) { ok = false; break; } + if (!type.ref().isObject()) continue; + zv::Val members; + if (instanceof_function(Z_OBJCE_P(type.raw()), pt_ce_union_type)) { + members = pt_union_type_get_types(Z_OBJ_P(type.raw())); + if (UNEXPECTED(members.isUndef())) { ok = false; break; } + } else { + zv::Arr single = zv::Arr::create(1); + single.push(zv::Val::copyOf(type.ref())); + members = zv::Val(std::move(single)); + } + bool captures = false; + for (auto member : zv::ArrRef(members.raw())) { + zval *memberType = member.value().deref().raw(); + if (Z_TYPE_P(memberType) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(memberType), pt_ce_closure_type)) continue; + zv::Val byRef = pt_closure_type_get_by_ref_use_types(memberType); + if (UNEXPECTED(byRef.isUndef())) { ok = false; break; } + for (auto use : zv::ArrRef(byRef.raw())) { + if (use.hasStringKey() && zend_hash_exists(&differing, use.stringKey())) { + captures = true; + break; + } + } + if (captures) break; + } + if (!ok) break; + if (captures) { + zv::Arr list = zv::Arr::adoptTable(zend_array_dup(Z_ARRVAL_P(names.raw()))); + list.push(zv::Ref(holder)); + names = zv::Val(std::move(list)); + } + } + if (!ok) break; + } + zend_hash_destroy(&differing); + return ok ? std::move(names) : zv::Val(); + } + /* Mirrors getVariableMentionFlow(). */ zv::Val getVariableMentionFlow(zval *stmt) { @@ -3225,6 +3447,21 @@ zv::Val pt_statements_handler_do_process_stmt_nodes(zval *handler, zval *nodeSco return pt_type_call(Z_OBJ_P(handler), PT_LC("doprocessstmtnodes"), 7, argv); } +zv::Val pt_statements_handler_process_loop_pass_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) +{ + if (isNativeHandler(handler)) return StatementsHandler(Z_OBJ_P(handler)).processLoopPassStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, previous, record); + zval recordRef; + ZVAL_NULL(&recordRef); + ZVAL_MAKE_REF(&recordRef); + zval null; + ZVAL_NULL(&null); + zv::Args argv{nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, previous != NULL ? previous : &null, &recordRef}; + zv::Val result = pt_type_call(Z_OBJ_P(handler), PT_LC("processlooppassstmtnodes"), 9, argv); + record = zv::Val::copyOf(zv::Ref(Z_REFVAL(recordRef))); + zval_ptr_dtor(&recordRef); + return result; +} + zv::Val pt_statements_handler_process_stmt_var_annotation(zval *handler, zval *nodeScopeResolver, zval *scope, zval *storage, zval *stmt, zval *defaultExpr, zval *nodeCallback) { if (isNativeHandler(handler)) return StatementsHandler(Z_OBJ_P(handler)).processStmtVarAnnotation(nodeScopeResolver, scope, storage, stmt, defaultExpr, nodeCallback); @@ -3312,6 +3549,26 @@ PT_MINIT_REGISTRATION(pt_register_statements_handler) PT_RETURN_VAL(StatementsHandler(Z_OBJ_P(ZEND_THIS)).doProcessStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context)); }); + cls.method(sigs::processLoopPassStmtNodes, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *nodeScopeResolver, *parentNode, *stmts, *scope, *storage, *nodeCallback, *context, *previous, *recordRef; + ZEND_PARSE_PARAMETERS_START(9, 9) + Z_PARAM_OBJECT(nodeScopeResolver) + Z_PARAM_OBJECT(parentNode) + Z_PARAM_ARRAY(stmts) + Z_PARAM_OBJECT(scope) + Z_PARAM_OBJECT(storage) + Z_PARAM_ZVAL(nodeCallback) + Z_PARAM_OBJECT(context) + Z_PARAM_ARRAY_OR_NULL(previous) + Z_PARAM_ZVAL(recordRef) + ZEND_PARSE_PARAMETERS_END(); + zv::Val record = zv::Val::null(); + zv::Val result = StatementsHandler(Z_OBJ_P(ZEND_THIS)).processLoopPassStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, previous, record); + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); + ZEND_TRY_ASSIGN_REF_COPY(recordRef, record.raw()); + PT_RETURN_VAL(std::move(result)); + }); + cls.method(sigs::getVariableMentionFlow, [](INTERNAL_FUNCTION_PARAMETERS) { zval *stmt; ZEND_PARSE_PARAMETERS_START(1, 1) diff --git a/turbo-ext/src/abi.h b/turbo-ext/src/abi.h index 615a8e04325..085d4b55d13 100644 --- a/turbo-ext/src/abi.h +++ b/turbo-ext/src/abi.h @@ -158,6 +158,7 @@ struct pt_abi_globals void (*call_stack_size_error)(); void (*try_assign_ref_str)(zval *zv, zend_string *str); void (*try_assign_ref_true)(zval *zv); + void (*try_assign_ref_copy)(zval *zv, zval *value); /* zend_wrong_parameter_error() with the neutral ZPP numbering (below) */ void (*wrong_parameter_error)(int errorCode, uint32_t num, char *name, int expectedType, zval *arg); /* the slow paths of the bool/double/string parameter parsing: 8.6 @@ -206,6 +207,7 @@ inline void pt_abi_weakrefs_hash_destroy(HashTable *ht) { pt_abi.weakrefs_hash_d inline void pt_abi_call_stack_size_error() { pt_abi.call_stack_size_error(); } inline void pt_abi_try_assign_ref_str(zval *zv, zend_string *str) { pt_abi.try_assign_ref_str(zv, str); } inline void pt_abi_try_assign_ref_true(zval *zv) { pt_abi.try_assign_ref_true(zv); } +inline void pt_abi_try_assign_ref_copy(zval *zv, zval *value) { pt_abi.try_assign_ref_copy(zv, value); } /* `then` from the given PHP_VERSION_ID on, `otherwise` before it — the * runtime form of an #if PHP_VERSION_ID gate */ @@ -571,6 +573,8 @@ static zend_always_inline void *pt_abi_object_alloc(size_t obj_size, zend_class_ #define ZEND_TRY_ASSIGN_REF_STR(zv, str) pt_abi_try_assign_ref_str(zv, str) #undef ZEND_TRY_ASSIGN_REF_TRUE #define ZEND_TRY_ASSIGN_REF_TRUE(zv) pt_abi_try_assign_ref_true(zv) +#undef ZEND_TRY_ASSIGN_REF_COPY +#define ZEND_TRY_ASSIGN_REF_COPY(zv, value) pt_abi_try_assign_ref_copy(zv, value) /* arg_info[i] of a user function (zend_arg_info; name and type keep their * offsets, the stride moved) */ diff --git a/turbo-ext/src/generated/InternalStatementExitPoint.h b/turbo-ext/src/generated/InternalStatementExitPoint.h index fe233feea32..c10a225e970 100644 --- a/turbo-ext/src/generated/InternalStatementExitPoint.h +++ b/turbo-ext/src/generated/InternalStatementExitPoint.h @@ -38,12 +38,16 @@ inline constexpr char strings[] = "PHPStan\\Analyser\\StatementExitPoint\0" /* 80 */ "toPublic\0" /* 116 */ "getStatement\0" /* 125 */ - "getScope"; /* 138 */ + "PHPStan\\Analyser\\InternalStatementExitPoint\0" /* 138 */ + "withScope\0" /* 182 */ + "getScope"; /* 192 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 10), /* __construct $statement */ reg::packed(30, 0, 36), /* __construct $scope */ reg::packed(79, 0, 80), /* toPublic return */ reg::packed(79, 0, 10), /* getStatement return */ + reg::packed(30, 0, 36), /* withScope $scope */ + reg::packed(79, 0, 138), /* withScope return */ reg::packed(79, 0, 36), /* getScope return */ }; using Sig = reg::Sig; @@ -54,7 +58,8 @@ namespace sig { inline constexpr sigtab::Sig __construct = { { 67 /* __construct */, 2, 0, 2, reg::NoArg, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig toPublic = { { 116 /* toPublic */, 0, 2, 0, 2, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig getStatement = { { 125 /* getStatement */, 0, 3, 0, 3, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getScope = { { 138 /* getScope */, 0, 4, 0, 4, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withScope = { { 182 /* withScope */, 1, 4, 1, 5, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getScope = { { 192 /* getScope */, 0, 6, 0, 6, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::InternalStatementExitPoint diff --git a/turbo-ext/src/generated/InternalThrowPoint.h b/turbo-ext/src/generated/InternalThrowPoint.h index 351d62a242e..9dd4fd7bfad 100644 --- a/turbo-ext/src/generated/InternalThrowPoint.h +++ b/turbo-ext/src/generated/InternalThrowPoint.h @@ -53,12 +53,13 @@ inline constexpr char strings[] = "createImplicit\0" /* 218 */ "throwPoint\0" /* 233 */ "createFromPublic\0" /* 244 */ - "getScope\0" /* 261 */ - "getType\0" /* 270 */ - "getNode\0" /* 278 */ - "isExplicit\0" /* 286 */ - "catchType\0" /* 297 */ - "subtractCatchType"; /* 307 */ + "withScope\0" /* 261 */ + "getScope\0" /* 271 */ + "getType\0" /* 280 */ + "getNode\0" /* 288 */ + "isExplicit\0" /* 296 */ + "catchType\0" /* 307 */ + "subtractCatchType"; /* 317 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 6), /* __construct $scope */ reg::packed(37, 0, 42), /* __construct $type */ @@ -78,11 +79,13 @@ inline constexpr reg::PackedArg args[] = { reg::packed(233, 0, 125), /* createFromPublic $throwPoint */ reg::packed(0, 0, 6), /* createFromPublic $scope */ reg::packed(124, 0, 162), /* createFromPublic return */ + reg::packed(0, 0, 6), /* withScope $scope */ + reg::packed(124, 0, 162), /* withScope return */ reg::packed(124, 0, 6), /* getScope return */ reg::packed(124, 0, 42), /* getType return */ reg::packed(124, MAY_BE_BOOL), /* isExplicit return */ reg::packed(124, MAY_BE_BOOL), /* canContainAnyThrowable return */ - reg::packed(297, 0, 42), /* subtractCatchType $catchType */ + reg::packed(307, 0, 42), /* subtractCatchType $catchType */ reg::packed(124, 0, 162), /* subtractCatchType return */ }; using Sig = reg::Sig; @@ -95,12 +98,13 @@ inline constexpr sigtab::Sig toPublic = { { 153 /* toPublic */, 0, 5, 0, 5, ZEND inline constexpr sigtab::Sig createExplicit = { { 198 /* createExplicit */, 4, 6, 4, 10, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; inline constexpr sigtab::Sig createImplicit = { { 218 /* createImplicit */, 2, 11, 3, 14, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; inline constexpr sigtab::Sig createFromPublic = { { 244 /* createFromPublic */, 2, 15, 2, 17, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig getScope = { { 261 /* getScope */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getType = { { 270 /* getType */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getNode = { { 278 /* getNode */, 0, 20, 0, reg::NoArg, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isExplicit = { { 286 /* isExplicit */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig canContainAnyThrowable = { { 89 /* canContainAnyThrowable */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig subtractCatchType = { { 307 /* subtractCatchType */, 1, 22, 1, 23, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withScope = { { 261 /* withScope */, 1, 18, 1, 19, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getScope = { { 271 /* getScope */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getType = { { 280 /* getType */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getNode = { { 288 /* getNode */, 0, 22, 0, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isExplicit = { { 296 /* isExplicit */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig canContainAnyThrowable = { { 89 /* canContainAnyThrowable */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig subtractCatchType = { { 317 /* subtractCatchType */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::InternalThrowPoint diff --git a/turbo-ext/src/generated/NodeScopeResolver.h b/turbo-ext/src/generated/NodeScopeResolver.h index 36b977e4b2a..aa8736e97ad 100644 --- a/turbo-ext/src/generated/NodeScopeResolver.h +++ b/turbo-ext/src/generated/NodeScopeResolver.h @@ -92,72 +92,75 @@ inline constexpr char strings[] = "PHPStan\\Analyser\\StatementContext\0" /* 864 */ "PHPStan\\Analyser\\StatementResult\0" /* 898 */ "processStmtNodes\0" /* 931 */ - "PHPStan\\Analyser\\InternalStatementResult\0" /* 948 */ - "processStmtNodesInternal\0" /* 989 */ - "stmt\0" /* 1014 */ - "PhpParser\\Node\\Stmt\0" /* 1019 */ - "processStmtNode\0" /* 1039 */ - "fileName\0" /* 1055 */ - "isAnalysedFile\0" /* 1064 */ - "isReturningStoredExpressionResults\0" /* 1079 */ - "isConsumingStoredExpressionResults\0" /* 1114 */ - "lookForSetAllowedUndefinedExpressions\0" /* 1149 */ - "lookForUnsetAllowedUndefinedExpressions\0" /* 1187 */ - "callback\0" /* 1227 */ - "Closure\0" /* 1236 */ - "lookForExpressionCallback\0" /* 1244 */ - "PHPStan\\Analyser\\ExpressionContext\0" /* 1270 */ - "processExprNodeConsumingStored\0" /* 1305 */ - "processExprOnDemand\0" /* 1336 */ - "readStoredResult\0" /* 1356 */ - "PHPStan\\Type\\Type\0" /* 1373 */ - "readTypeOfMaybeStored\0" /* 1391 */ - "findScopeStateType\0" /* 1413 */ - "readScopeStateOrSyntheticType\0" /* 1432 */ - "requireScopeStateType\0" /* 1462 */ - "caller\0" /* 1484 */ - "guardAgainstUnprocessedRealNode\0" /* 1491 */ - "processSyntheticOnDemand\0" /* 1523 */ - "processExprNode\0" /* 1548 */ - "processExprNodeInternal\0" /* 1564 */ - "propertyFetch\0" /* 1588 */ - "PhpParser\\Node\\Expr\\PropertyFetch\0" /* 1602 */ - "propertyReflection\0" /* 1636 */ - "PHPStan\\Reflection\\Php\\PhpPropertyReflection\0" /* 1655 */ - "hookName\0" /* 1700 */ - "getImpurePointsFromPropertyHook\0" /* 1709 */ - "isPropertyHookBackingValueAccess\0" /* 1741 */ - "getAssignedVariables\0" /* 1774 */ - "loopNode\0" /* 1795 */ - "bodyStmts\0" /* 1804 */ - "isReplayableConvergenceBody\0" /* 1814 */ - "node\0" /* 1842 */ - "hasContextSensitiveConstruct\0" /* 1847 */ - "gatherer\0" /* 1876 */ - "pushNodeGatherer\0" /* 1885 */ - "popNodeGatherer\0" /* 1902 */ - "suspendNodeGatherers\0" /* 1918 */ - "gatherers\0" /* 1939 */ - "restoreNodeGatherers\0" /* 1949 */ - "recording\0" /* 1970 */ - "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 1980 */ - "replayRecording\0" /* 2019 */ - "from\0" /* 2035 */ - "to\0" /* 2040 */ - "replayRecordingRange\0" /* 2043 */ - "callNodeCallbackWithExpression\0" /* 2064 */ - "callNodeCallback\0" /* 2095 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 2112 */ - "observingTemplateArgumentFrame\0" /* 2160 */ - "returnedResult\0" /* 2191 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 2206 */ - "collectReturnSend\0" /* 2260 */ - "keyType\0" /* 2278 */ - "valueType\0" /* 2286 */ - "collectYieldSend\0" /* 2296 */ - "delegatedType\0" /* 2313 */ - "collectYieldFromSend\0" /* 2327 */ - "getDeclaredReturnType"; /* 2348 */ + "previous\0" /* 948 */ + "record\0" /* 957 */ + "PHPStan\\Analyser\\InternalStatementResult\0" /* 964 */ + "processLoopPassStmtNodes\0" /* 1005 */ + "processStmtNodesInternal\0" /* 1030 */ + "stmt\0" /* 1055 */ + "PhpParser\\Node\\Stmt\0" /* 1060 */ + "processStmtNode\0" /* 1080 */ + "fileName\0" /* 1096 */ + "isAnalysedFile\0" /* 1105 */ + "isReturningStoredExpressionResults\0" /* 1120 */ + "isConsumingStoredExpressionResults\0" /* 1155 */ + "lookForSetAllowedUndefinedExpressions\0" /* 1190 */ + "lookForUnsetAllowedUndefinedExpressions\0" /* 1228 */ + "callback\0" /* 1268 */ + "Closure\0" /* 1277 */ + "lookForExpressionCallback\0" /* 1285 */ + "PHPStan\\Analyser\\ExpressionContext\0" /* 1311 */ + "processExprNodeConsumingStored\0" /* 1346 */ + "processExprOnDemand\0" /* 1377 */ + "readStoredResult\0" /* 1397 */ + "PHPStan\\Type\\Type\0" /* 1414 */ + "readTypeOfMaybeStored\0" /* 1432 */ + "findScopeStateType\0" /* 1454 */ + "readScopeStateOrSyntheticType\0" /* 1473 */ + "requireScopeStateType\0" /* 1503 */ + "caller\0" /* 1525 */ + "guardAgainstUnprocessedRealNode\0" /* 1532 */ + "processSyntheticOnDemand\0" /* 1564 */ + "processExprNode\0" /* 1589 */ + "processExprNodeInternal\0" /* 1605 */ + "propertyFetch\0" /* 1629 */ + "PhpParser\\Node\\Expr\\PropertyFetch\0" /* 1643 */ + "propertyReflection\0" /* 1677 */ + "PHPStan\\Reflection\\Php\\PhpPropertyReflection\0" /* 1696 */ + "hookName\0" /* 1741 */ + "getImpurePointsFromPropertyHook\0" /* 1750 */ + "isPropertyHookBackingValueAccess\0" /* 1782 */ + "getAssignedVariables\0" /* 1815 */ + "loopNode\0" /* 1836 */ + "bodyStmts\0" /* 1845 */ + "isReplayableConvergenceBody\0" /* 1855 */ + "node\0" /* 1883 */ + "hasContextSensitiveConstruct\0" /* 1888 */ + "gatherer\0" /* 1917 */ + "pushNodeGatherer\0" /* 1926 */ + "popNodeGatherer\0" /* 1943 */ + "suspendNodeGatherers\0" /* 1959 */ + "gatherers\0" /* 1980 */ + "restoreNodeGatherers\0" /* 1990 */ + "recording\0" /* 2011 */ + "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 2021 */ + "replayRecording\0" /* 2060 */ + "from\0" /* 2076 */ + "to\0" /* 2081 */ + "replayRecordingRange\0" /* 2084 */ + "callNodeCallbackWithExpression\0" /* 2105 */ + "callNodeCallback\0" /* 2136 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 2153 */ + "observingTemplateArgumentFrame\0" /* 2201 */ + "returnedResult\0" /* 2232 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 2247 */ + "collectReturnSend\0" /* 2301 */ + "keyType\0" /* 2319 */ + "valueType\0" /* 2327 */ + "collectYieldSend\0" /* 2337 */ + "delegatedType\0" /* 2354 */ + "collectYieldFromSend\0" /* 2368 */ + "getDeclaredReturnType"; /* 2389 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 10), /* __construct $container */ reg::packed(48, 0, 73), /* __construct $templateArgumentObserver */ @@ -191,20 +194,29 @@ inline constexpr reg::PackedArg args[] = { reg::packed(539, MAY_BE_CALLABLE), /* processStmtNodes $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNodes $context */ reg::packed(371, 0, 898), /* processStmtNodes return */ + reg::packed(832, 0, 843), /* processLoopPassStmtNodes $parentNode */ + reg::packed(858, MAY_BE_ARRAY), /* processLoopPassStmtNodes $stmts */ + reg::packed(502, 0, 508), /* processLoopPassStmtNodes $scope */ + reg::packed(614, 0, 622), /* processLoopPassStmtNodes $storage */ + reg::packed(539, MAY_BE_CALLABLE), /* processLoopPassStmtNodes $nodeCallback */ + reg::packed(761, 0, 864), /* processLoopPassStmtNodes $context */ + reg::packed(948, MAY_BE_NULL | MAY_BE_ARRAY), /* processLoopPassStmtNodes $previous */ + reg::packed(957, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, true, false), /* processLoopPassStmtNodes $record */ + reg::packed(371, 0, 964), /* processLoopPassStmtNodes return */ reg::packed(832, 0, 843), /* processStmtNodesInternal $parentNode */ reg::packed(858, MAY_BE_ARRAY), /* processStmtNodesInternal $stmts */ reg::packed(502, 0, 508), /* processStmtNodesInternal $scope */ reg::packed(614, 0, 622), /* processStmtNodesInternal $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processStmtNodesInternal $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNodesInternal $context */ - reg::packed(371, 0, 948), /* processStmtNodesInternal return */ - reg::packed(1014, 0, 1019), /* processStmtNode $stmt */ + reg::packed(371, 0, 964), /* processStmtNodesInternal return */ + reg::packed(1055, 0, 1060), /* processStmtNode $stmt */ reg::packed(502, 0, 508), /* processStmtNode $scope */ reg::packed(614, 0, 622), /* processStmtNode $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processStmtNode $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNode $context */ - reg::packed(371, 0, 948), /* processStmtNode return */ - reg::packed(1055, MAY_BE_STRING), /* isAnalysedFile $fileName */ + reg::packed(371, 0, 964), /* processStmtNode return */ + reg::packed(1096, MAY_BE_STRING), /* isAnalysedFile $fileName */ reg::packed(371, MAY_BE_BOOL), /* isAnalysedFile return */ reg::packed(371, MAY_BE_BOOL), /* isReturningStoredExpressionResults return */ reg::packed(371, MAY_BE_BOOL), /* isConsumingStoredExpressionResults return */ @@ -216,14 +228,14 @@ inline constexpr reg::PackedArg args[] = { reg::packed(371, 0, 508), /* lookForUnsetAllowedUndefinedExpressions return */ reg::packed(502, 0, 508), /* lookForExpressionCallback $scope */ reg::packed(663, 0, 668), /* lookForExpressionCallback $expr */ - reg::packed(1227, 0, 1236), /* lookForExpressionCallback $callback */ + reg::packed(1268, 0, 1277), /* lookForExpressionCallback $callback */ reg::packed(371, 0, 508), /* lookForExpressionCallback return */ - reg::packed(1014, 0, 1019), /* processExprNodeConsumingStored $stmt */ + reg::packed(1055, 0, 1060), /* processExprNodeConsumingStored $stmt */ reg::packed(663, 0, 668), /* processExprNodeConsumingStored $expr */ reg::packed(502, 0, 508), /* processExprNodeConsumingStored $scope */ reg::packed(614, 0, 622), /* processExprNodeConsumingStored $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNodeConsumingStored $nodeCallback */ - reg::packed(761, 0, 1270), /* processExprNodeConsumingStored $context */ + reg::packed(761, 0, 1311), /* processExprNodeConsumingStored $context */ reg::packed(371, 0, 705), /* processExprNodeConsumingStored return */ reg::packed(663, 0, 668), /* processExprOnDemand $expr */ reg::packed(502, 0, 508), /* processExprOnDemand $scope */ @@ -234,65 +246,65 @@ inline constexpr reg::PackedArg args[] = { reg::packed(371, 0, 705), /* readStoredResult return */ reg::packed(663, 0, 668), /* readTypeOfMaybeStored $expr */ reg::packed(502, 0, 508), /* readTypeOfMaybeStored $scope */ - reg::packed(371, 0, 1373), /* readTypeOfMaybeStored return */ + reg::packed(371, 0, 1414), /* readTypeOfMaybeStored return */ reg::packed(663, 0, 668), /* findScopeStateType $expr */ reg::packed(502, 0, 508), /* findScopeStateType $scope */ - reg::packed(371, MAY_BE_NULL, 1373), /* findScopeStateType return */ + reg::packed(371, MAY_BE_NULL, 1414), /* findScopeStateType return */ reg::packed(663, 0, 668), /* readScopeStateOrSyntheticType $expr */ reg::packed(502, 0, 508), /* readScopeStateOrSyntheticType $scope */ - reg::packed(371, 0, 1373), /* readScopeStateOrSyntheticType return */ + reg::packed(371, 0, 1414), /* readScopeStateOrSyntheticType return */ reg::packed(663, 0, 668), /* requireScopeStateType $expr */ reg::packed(502, 0, 508), /* requireScopeStateType $scope */ - reg::packed(371, 0, 1373), /* requireScopeStateType return */ + reg::packed(371, 0, 1414), /* requireScopeStateType return */ reg::packed(663, 0, 668), /* guardAgainstUnprocessedRealNode $expr */ - reg::packed(1484, MAY_BE_STRING), /* guardAgainstUnprocessedRealNode $caller */ + reg::packed(1525, MAY_BE_STRING), /* guardAgainstUnprocessedRealNode $caller */ reg::packed(371, MAY_BE_VOID), /* guardAgainstUnprocessedRealNode return */ reg::packed(663, 0, 668), /* processSyntheticOnDemand $expr */ reg::packed(502, 0, 508), /* processSyntheticOnDemand $scope */ reg::packed(371, 0, 705), /* processSyntheticOnDemand return */ - reg::packed(1014, 0, 1019), /* processExprNode $stmt */ + reg::packed(1055, 0, 1060), /* processExprNode $stmt */ reg::packed(663, 0, 668), /* processExprNode $expr */ reg::packed(502, 0, 508), /* processExprNode $scope */ reg::packed(614, 0, 622), /* processExprNode $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNode $nodeCallback */ - reg::packed(761, 0, 1270), /* processExprNode $context */ + reg::packed(761, 0, 1311), /* processExprNode $context */ reg::packed(371, 0, 705), /* processExprNode return */ - reg::packed(1014, 0, 1019), /* processExprNodeInternal $stmt */ + reg::packed(1055, 0, 1060), /* processExprNodeInternal $stmt */ reg::packed(663, 0, 668), /* processExprNodeInternal $expr */ reg::packed(502, 0, 508), /* processExprNodeInternal $scope */ reg::packed(614, 0, 622), /* processExprNodeInternal $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNodeInternal $nodeCallback */ - reg::packed(761, 0, 1270), /* processExprNodeInternal $context */ + reg::packed(761, 0, 1311), /* processExprNodeInternal $context */ reg::packed(371, 0, 705), /* processExprNodeInternal return */ reg::packed(502, 0, 508), /* getImpurePointsFromPropertyHook $scope */ - reg::packed(1588, 0, 1602), /* getImpurePointsFromPropertyHook $propertyFetch */ - reg::packed(1636, 0, 1655), /* getImpurePointsFromPropertyHook $propertyReflection */ - reg::packed(1700, MAY_BE_STRING), /* getImpurePointsFromPropertyHook $hookName */ + reg::packed(1629, 0, 1643), /* getImpurePointsFromPropertyHook $propertyFetch */ + reg::packed(1677, 0, 1696), /* getImpurePointsFromPropertyHook $propertyReflection */ + reg::packed(1741, MAY_BE_STRING), /* getImpurePointsFromPropertyHook $hookName */ reg::packed(371, MAY_BE_ARRAY), /* getImpurePointsFromPropertyHook return */ reg::packed(502, 0, 508), /* isPropertyHookBackingValueAccess $scope */ - reg::packed(1588, 0, 1602), /* isPropertyHookBackingValueAccess $propertyFetch */ + reg::packed(1629, 0, 1643), /* isPropertyHookBackingValueAccess $propertyFetch */ reg::packed(371, MAY_BE_BOOL), /* isPropertyHookBackingValueAccess return */ reg::packed(663, 0, 668), /* getAssignedVariables $expr */ reg::packed(371, MAY_BE_ARRAY), /* getAssignedVariables return */ - reg::packed(1795, 0, 843), /* isReplayableConvergenceBody $loopNode */ - reg::packed(1804, MAY_BE_ARRAY), /* isReplayableConvergenceBody $bodyStmts */ + reg::packed(1836, 0, 843), /* isReplayableConvergenceBody $loopNode */ + reg::packed(1845, MAY_BE_ARRAY), /* isReplayableConvergenceBody $bodyStmts */ reg::packed(371, MAY_BE_BOOL), /* isReplayableConvergenceBody return */ - reg::packed(1842, 0, 843), /* hasContextSensitiveConstruct $node */ + reg::packed(1883, 0, 843), /* hasContextSensitiveConstruct $node */ reg::packed(371, MAY_BE_BOOL), /* hasContextSensitiveConstruct return */ - reg::packed(1876, MAY_BE_CALLABLE), /* pushNodeGatherer $gatherer */ + reg::packed(1917, MAY_BE_CALLABLE), /* pushNodeGatherer $gatherer */ reg::packed(371, MAY_BE_VOID), /* pushNodeGatherer return */ reg::packed(371, MAY_BE_VOID), /* popNodeGatherer return */ reg::packed(371, MAY_BE_ARRAY), /* suspendNodeGatherers return */ - reg::packed(1939, MAY_BE_ARRAY), /* restoreNodeGatherers $gatherers */ + reg::packed(1980, MAY_BE_ARRAY), /* restoreNodeGatherers $gatherers */ reg::packed(371, MAY_BE_VOID), /* restoreNodeGatherers return */ - reg::packed(1970, 0, 1980), /* replayRecording $recording */ + reg::packed(2011, 0, 2021), /* replayRecording $recording */ reg::packed(539, MAY_BE_CALLABLE), /* replayRecording $nodeCallback */ reg::packed(614, 0, 622), /* replayRecording $storage */ reg::packed(502, 0, 508), /* replayRecording $scope */ reg::packed(371, MAY_BE_VOID), /* replayRecording return */ - reg::packed(1970, 0, 1980), /* replayRecordingRange $recording */ - reg::packed(2035, MAY_BE_LONG), /* replayRecordingRange $from */ - reg::packed(2040, MAY_BE_LONG), /* replayRecordingRange $to */ + reg::packed(2011, 0, 2021), /* replayRecordingRange $recording */ + reg::packed(2076, MAY_BE_LONG), /* replayRecordingRange $from */ + reg::packed(2081, MAY_BE_LONG), /* replayRecordingRange $to */ reg::packed(539, MAY_BE_CALLABLE), /* replayRecordingRange $nodeCallback */ reg::packed(614, 0, 622), /* replayRecordingRange $storage */ reg::packed(502, 0, 508), /* replayRecordingRange $scope */ @@ -301,27 +313,27 @@ inline constexpr reg::PackedArg args[] = { reg::packed(663, 0, 843), /* callNodeCallbackWithExpression $expr */ reg::packed(502, 0, 508), /* callNodeCallbackWithExpression $scope */ reg::packed(614, 0, 622), /* callNodeCallbackWithExpression $storage */ - reg::packed(761, 0, 1270), /* callNodeCallbackWithExpression $context */ + reg::packed(761, 0, 1311), /* callNodeCallbackWithExpression $context */ reg::packed(371, MAY_BE_VOID), /* callNodeCallbackWithExpression return */ reg::packed(539, MAY_BE_CALLABLE), /* callNodeCallback $nodeCallback */ - reg::packed(1842, 0, 843), /* callNodeCallback $node */ + reg::packed(1883, 0, 843), /* callNodeCallback $node */ reg::packed(502, 0, 508), /* callNodeCallback $scope */ reg::packed(614, 0, 622), /* callNodeCallback $storage */ reg::packed(371, MAY_BE_VOID), /* callNodeCallback return */ reg::packed(502, 0, 508), /* observingTemplateArgumentFrame $scope */ - reg::packed(371, MAY_BE_NULL, 2112), /* observingTemplateArgumentFrame return */ + reg::packed(371, MAY_BE_NULL, 2153), /* observingTemplateArgumentFrame return */ reg::packed(502, 0, 508), /* collectReturnSend $scope */ - reg::packed(2191, 0, 705), /* collectReturnSend $returnedResult */ - reg::packed(371, 0, 2206), /* collectReturnSend return */ + reg::packed(2232, 0, 705), /* collectReturnSend $returnedResult */ + reg::packed(371, 0, 2247), /* collectReturnSend return */ reg::packed(502, 0, 508), /* collectYieldSend $scope */ - reg::packed(2278, MAY_BE_NULL, 1373), /* collectYieldSend $keyType */ - reg::packed(2286, MAY_BE_NULL, 1373), /* collectYieldSend $valueType */ - reg::packed(371, 0, 2206), /* collectYieldSend return */ + reg::packed(2319, MAY_BE_NULL, 1414), /* collectYieldSend $keyType */ + reg::packed(2327, MAY_BE_NULL, 1414), /* collectYieldSend $valueType */ + reg::packed(371, 0, 2247), /* collectYieldSend return */ reg::packed(502, 0, 508), /* collectYieldFromSend $scope */ - reg::packed(2313, 0, 1373), /* collectYieldFromSend $delegatedType */ - reg::packed(371, 0, 2206), /* collectYieldFromSend return */ + reg::packed(2354, 0, 1414), /* collectYieldFromSend $delegatedType */ + reg::packed(371, 0, 2247), /* collectYieldFromSend return */ reg::packed(502, 0, 508), /* getDeclaredReturnType $scope */ - reg::packed(371, MAY_BE_NULL, 1373), /* getDeclaredReturnType return */ + reg::packed(371, MAY_BE_NULL, 1414), /* getDeclaredReturnType return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -337,43 +349,44 @@ inline constexpr sigtab::Sig processFileNodes = { { 597 /* processFileNodes */, inline constexpr sigtab::Sig storeExpressionResult = { { 739 /* storeExpressionResult */, 3, 18, 3, 21, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig narrowScopeWithCondition = { { 807 /* narrowScopeWithCondition */, 3, 22, 3, 25, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig processStmtNodes = { { 931 /* processStmtNodes */, 5, 26, 5, 31, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtNodesInternal = { { 989 /* processStmtNodesInternal */, 6, 32, 6, 38, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtNode = { { 1039 /* processStmtNode */, 5, 39, 5, 44, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isAnalysedFile = { { 1064 /* isAnalysedFile */, 1, 45, 1, 46, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isReturningStoredExpressionResults = { { 1079 /* isReturningStoredExpressionResults */, 0, 47, 0, 47, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isConsumingStoredExpressionResults = { { 1114 /* isConsumingStoredExpressionResults */, 0, 48, 0, 48, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForSetAllowedUndefinedExpressions = { { 1149 /* lookForSetAllowedUndefinedExpressions */, 2, 49, 2, 51, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForUnsetAllowedUndefinedExpressions = { { 1187 /* lookForUnsetAllowedUndefinedExpressions */, 2, 52, 2, 54, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForExpressionCallback = { { 1244 /* lookForExpressionCallback */, 3, 55, 3, 58, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processExprNodeConsumingStored = { { 1305 /* processExprNodeConsumingStored */, 6, 59, 6, 65, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprOnDemand = { { 1336 /* processExprOnDemand */, 3, 66, 3, 69, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readStoredResult = { { 1356 /* readStoredResult */, 2, 70, 2, 72, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readTypeOfMaybeStored = { { 1391 /* readTypeOfMaybeStored */, 2, 73, 2, 75, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findScopeStateType = { { 1413 /* findScopeStateType */, 2, 76, 2, 78, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readScopeStateOrSyntheticType = { { 1432 /* readScopeStateOrSyntheticType */, 2, 79, 2, 81, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig requireScopeStateType = { { 1462 /* requireScopeStateType */, 2, 82, 2, 84, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig guardAgainstUnprocessedRealNode = { { 1491 /* guardAgainstUnprocessedRealNode */, 2, 85, 2, 87, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processSyntheticOnDemand = { { 1523 /* processSyntheticOnDemand */, 2, 88, 2, 90, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprNode = { { 1548 /* processExprNode */, 6, 91, 6, 97, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprNodeInternal = { { 1564 /* processExprNodeInternal */, 6, 98, 6, 104, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getImpurePointsFromPropertyHook = { { 1709 /* getImpurePointsFromPropertyHook */, 4, 105, 4, 109, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPropertyHookBackingValueAccess = { { 1741 /* isPropertyHookBackingValueAccess */, 2, 110, 2, 112, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getAssignedVariables = { { 1774 /* getAssignedVariables */, 1, 113, 1, 114, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isReplayableConvergenceBody = { { 1814 /* isReplayableConvergenceBody */, 2, 115, 2, 117, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasContextSensitiveConstruct = { { 1847 /* hasContextSensitiveConstruct */, 1, 118, 1, 119, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig pushNodeGatherer = { { 1885 /* pushNodeGatherer */, 1, 120, 1, 121, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig popNodeGatherer = { { 1902 /* popNodeGatherer */, 0, 122, 0, 122, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig suspendNodeGatherers = { { 1918 /* suspendNodeGatherers */, 0, 123, 0, 123, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig restoreNodeGatherers = { { 1949 /* restoreNodeGatherers */, 1, 124, 1, 125, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig replayRecording = { { 2019 /* replayRecording */, 4, 126, 4, 130, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig replayRecordingRange = { { 2043 /* replayRecordingRange */, 6, 131, 6, 137, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig callNodeCallbackWithExpression = { { 2064 /* callNodeCallbackWithExpression */, 5, 138, 5, 143, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig callNodeCallback = { { 2095 /* callNodeCallback */, 4, 144, 4, 148, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig observingTemplateArgumentFrame = { { 2160 /* observingTemplateArgumentFrame */, 1, 149, 1, 150, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectReturnSend = { { 2260 /* collectReturnSend */, 2, 151, 2, 153, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectYieldSend = { { 2296 /* collectYieldSend */, 3, 154, 3, 157, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectYieldFromSend = { { 2327 /* collectYieldFromSend */, 2, 158, 2, 160, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDeclaredReturnType = { { 2348 /* getDeclaredReturnType */, 1, 161, 1, 162, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig processLoopPassStmtNodes = { { 1005 /* processLoopPassStmtNodes */, 8, 32, 8, 40, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtNodesInternal = { { 1030 /* processStmtNodesInternal */, 6, 41, 6, 47, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtNode = { { 1080 /* processStmtNode */, 5, 48, 5, 53, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isAnalysedFile = { { 1105 /* isAnalysedFile */, 1, 54, 1, 55, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isReturningStoredExpressionResults = { { 1120 /* isReturningStoredExpressionResults */, 0, 56, 0, 56, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isConsumingStoredExpressionResults = { { 1155 /* isConsumingStoredExpressionResults */, 0, 57, 0, 57, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForSetAllowedUndefinedExpressions = { { 1190 /* lookForSetAllowedUndefinedExpressions */, 2, 58, 2, 60, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForUnsetAllowedUndefinedExpressions = { { 1228 /* lookForUnsetAllowedUndefinedExpressions */, 2, 61, 2, 63, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForExpressionCallback = { { 1285 /* lookForExpressionCallback */, 3, 64, 3, 67, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processExprNodeConsumingStored = { { 1346 /* processExprNodeConsumingStored */, 6, 68, 6, 74, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprOnDemand = { { 1377 /* processExprOnDemand */, 3, 75, 3, 78, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readStoredResult = { { 1397 /* readStoredResult */, 2, 79, 2, 81, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readTypeOfMaybeStored = { { 1432 /* readTypeOfMaybeStored */, 2, 82, 2, 84, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findScopeStateType = { { 1454 /* findScopeStateType */, 2, 85, 2, 87, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readScopeStateOrSyntheticType = { { 1473 /* readScopeStateOrSyntheticType */, 2, 88, 2, 90, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig requireScopeStateType = { { 1503 /* requireScopeStateType */, 2, 91, 2, 93, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig guardAgainstUnprocessedRealNode = { { 1532 /* guardAgainstUnprocessedRealNode */, 2, 94, 2, 96, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processSyntheticOnDemand = { { 1564 /* processSyntheticOnDemand */, 2, 97, 2, 99, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprNode = { { 1589 /* processExprNode */, 6, 100, 6, 106, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprNodeInternal = { { 1605 /* processExprNodeInternal */, 6, 107, 6, 113, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getImpurePointsFromPropertyHook = { { 1750 /* getImpurePointsFromPropertyHook */, 4, 114, 4, 118, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPropertyHookBackingValueAccess = { { 1782 /* isPropertyHookBackingValueAccess */, 2, 119, 2, 121, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getAssignedVariables = { { 1815 /* getAssignedVariables */, 1, 122, 1, 123, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isReplayableConvergenceBody = { { 1855 /* isReplayableConvergenceBody */, 2, 124, 2, 126, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasContextSensitiveConstruct = { { 1888 /* hasContextSensitiveConstruct */, 1, 127, 1, 128, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig pushNodeGatherer = { { 1926 /* pushNodeGatherer */, 1, 129, 1, 130, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig popNodeGatherer = { { 1943 /* popNodeGatherer */, 0, 131, 0, 131, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig suspendNodeGatherers = { { 1959 /* suspendNodeGatherers */, 0, 132, 0, 132, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig restoreNodeGatherers = { { 1990 /* restoreNodeGatherers */, 1, 133, 1, 134, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig replayRecording = { { 2060 /* replayRecording */, 4, 135, 4, 139, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig replayRecordingRange = { { 2084 /* replayRecordingRange */, 6, 140, 6, 146, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callNodeCallbackWithExpression = { { 2105 /* callNodeCallbackWithExpression */, 5, 147, 5, 152, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callNodeCallback = { { 2136 /* callNodeCallback */, 4, 153, 4, 157, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig observingTemplateArgumentFrame = { { 2201 /* observingTemplateArgumentFrame */, 1, 158, 1, 159, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectReturnSend = { { 2301 /* collectReturnSend */, 2, 160, 2, 162, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectYieldSend = { { 2337 /* collectYieldSend */, 3, 163, 3, 166, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectYieldFromSend = { { 2368 /* collectYieldFromSend */, 2, 167, 2, 169, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDeclaredReturnType = { { 2389 /* getDeclaredReturnType */, 1, 170, 1, 171, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; } // namespace sig } // namespace ptdecl::NodeScopeResolver diff --git a/turbo-ext/src/generated/StatementsHandler.h b/turbo-ext/src/generated/StatementsHandler.h index 87d5abad04b..13ab5d8c2cf 100644 --- a/turbo-ext/src/generated/StatementsHandler.h +++ b/turbo-ext/src/generated/StatementsHandler.h @@ -83,80 +83,87 @@ inline constexpr char strings[] = "stmts\0" /* 897 */ "PHPStan\\Analyser\\InternalStatementResult\0" /* 903 */ "doProcessStmtNodes\0" /* 944 */ - "i\0" /* 963 */ - "stmt\0" /* 965 */ - "PhpParser\\Node\\Stmt\0" /* 970 */ - "state\0" /* 990 */ - "PHPStan\\Analyser\\StatementListWalkState\0" /* 996 */ - "shouldCheckLastStatement\0" /* 1036 */ - "processStatementStep\0" /* 1061 */ - "PHPStan\\Analyser\\ExpressionResult\0" /* 1082 */ - "readEndStatementExprResult\0" /* 1116 */ - "processBodyStmtNodesTwoPass\0" /* 1143 */ - "staticSites\0" /* 1171 */ - "staticStatementIndexes\0" /* 1183 */ - "parentFrame\0" /* 1206 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 1218 */ - "entries\0" /* 1266 */ - "recording\0" /* 1274 */ - "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 1284 */ - "inferStaticVariableTypes\0" /* 1323 */ - "staticWalk\0" /* 1348 */ - "frame\0" /* 1359 */ - "parentConstraints\0" /* 1365 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 1383 */ - "replayStaticVariableWalk\0" /* 1437 */ - "bodyScope\0" /* 1462 */ - "endState\0" /* 1472 */ - "moreScopes\0" /* 1481 */ - "collectStaticVariableScopes\0" /* 1492 */ - "collectStaticVariableStateScopes\0" /* 1520 */ - "names\0" /* 1553 */ - "scopes\0" /* 1559 */ - "collectStaticVariableTypes\0" /* 1566 */ - "runs\0" /* 1593 */ - "types\0" /* 1598 */ - "walkedConditionalExpressions\0" /* 1604 */ - "enteredTypes\0" /* 1633 */ - "collectStaticVariableConditionalExpressions\0" /* 1646 */ - "isWalkedStaticVariableState\0" /* 1690 */ - "a\0" /* 1718 */ - "b\0" /* 1720 */ - "equalStaticVariableStates\0" /* 1722 */ - "equalStaticVariableConditionalExpressions\0" /* 1748 */ - "PHPStan\\Analyser\\ConditionalExpressionHolder\0" /* 1790 */ - "equalConditionalExpressionHolders\0" /* 1835 */ - "walkTypes\0" /* 1869 */ - "generalize\0" /* 1879 */ - "joinStaticVariableTypes\0" /* 1890 */ - "PHPStan\\Analyser\\ClosureProcessor\0" /* 1914 */ - "getClosureProcessor\0" /* 1948 */ - "processDeferredByRefClosureBodies\0" /* 1968 */ - "statementStartTokenPositions\0" /* 2002 */ - "observeClosureSignatures\0" /* 2031 */ - "recordedEntry\0" /* 2056 */ - "recordedExit\0" /* 2070 */ - "withRecordedConstraints\0" /* 2083 */ - "containsLabels\0" /* 2107 */ - "from\0" /* 2122 */ - "to\0" /* 2127 */ - "appendRecordedStatementResults\0" /* 2130 */ - "PHPStan\\Analyser\\VariableFlow\0" /* 2161 */ - "getVariableMentionFlow\0" /* 2191 */ - "variableNames\0" /* 2214 */ - "statementMentionsAnyVariable\0" /* 2228 */ - "node\0" /* 2257 */ - "mentionsEverything\0" /* 2262 */ - "collectMentionedVariables\0" /* 2281 */ - "getVarTagDependencies\0" /* 2307 */ - "statement\0" /* 2329 */ - "getOverridingThrowPoints\0" /* 2339 */ - "defaultExpr\0" /* 2364 */ - "PhpParser\\Node\\Expr\0" /* 2376 */ - "processStmtVarAnnotation\0" /* 2396 */ - "PHPStan\\PhpDoc\\Tag\\VarTag\0" /* 2421 */ - "findSingleVariableLessVarTag\0" /* 2447 */ - "emitVarTagChangedNode"; /* 2476 */ + "previous\0" /* 963 */ + "record\0" /* 972 */ + "processLoopPassStmtNodes\0" /* 979 */ + "variableNames\0" /* 1004 */ + "withByRefClosureHolders\0" /* 1018 */ + "state\0" /* 1042 */ + "PHPStan\\Analyser\\StatementListWalkState\0" /* 1048 */ + "exitPointCount\0" /* 1088 */ + "throwPointCount\0" /* 1103 */ + "recordedEntry\0" /* 1119 */ + "rebaseAppendedPoints\0" /* 1133 */ + "i\0" /* 1154 */ + "stmt\0" /* 1156 */ + "PhpParser\\Node\\Stmt\0" /* 1161 */ + "shouldCheckLastStatement\0" /* 1181 */ + "processStatementStep\0" /* 1206 */ + "PHPStan\\Analyser\\ExpressionResult\0" /* 1227 */ + "readEndStatementExprResult\0" /* 1261 */ + "processBodyStmtNodesTwoPass\0" /* 1288 */ + "staticSites\0" /* 1316 */ + "staticStatementIndexes\0" /* 1328 */ + "parentFrame\0" /* 1351 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 1363 */ + "entries\0" /* 1411 */ + "recording\0" /* 1419 */ + "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 1429 */ + "inferStaticVariableTypes\0" /* 1468 */ + "staticWalk\0" /* 1493 */ + "frame\0" /* 1504 */ + "parentConstraints\0" /* 1510 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 1528 */ + "replayStaticVariableWalk\0" /* 1582 */ + "bodyScope\0" /* 1607 */ + "endState\0" /* 1617 */ + "moreScopes\0" /* 1626 */ + "collectStaticVariableScopes\0" /* 1637 */ + "collectStaticVariableStateScopes\0" /* 1665 */ + "names\0" /* 1698 */ + "scopes\0" /* 1704 */ + "collectStaticVariableTypes\0" /* 1711 */ + "runs\0" /* 1738 */ + "types\0" /* 1743 */ + "walkedConditionalExpressions\0" /* 1749 */ + "enteredTypes\0" /* 1778 */ + "collectStaticVariableConditionalExpressions\0" /* 1791 */ + "isWalkedStaticVariableState\0" /* 1835 */ + "a\0" /* 1863 */ + "b\0" /* 1865 */ + "equalStaticVariableStates\0" /* 1867 */ + "equalStaticVariableConditionalExpressions\0" /* 1893 */ + "PHPStan\\Analyser\\ConditionalExpressionHolder\0" /* 1935 */ + "equalConditionalExpressionHolders\0" /* 1980 */ + "walkTypes\0" /* 2014 */ + "generalize\0" /* 2024 */ + "joinStaticVariableTypes\0" /* 2035 */ + "PHPStan\\Analyser\\ClosureProcessor\0" /* 2059 */ + "getClosureProcessor\0" /* 2093 */ + "processDeferredByRefClosureBodies\0" /* 2113 */ + "statementStartTokenPositions\0" /* 2147 */ + "observeClosureSignatures\0" /* 2176 */ + "recordedExit\0" /* 2201 */ + "withRecordedConstraints\0" /* 2214 */ + "containsLabels\0" /* 2238 */ + "from\0" /* 2253 */ + "to\0" /* 2258 */ + "appendRecordedStatementResults\0" /* 2261 */ + "PHPStan\\Analyser\\VariableFlow\0" /* 2292 */ + "getVariableMentionFlow\0" /* 2322 */ + "statementMentionsAnyVariable\0" /* 2345 */ + "node\0" /* 2374 */ + "mentionsEverything\0" /* 2379 */ + "collectMentionedVariables\0" /* 2398 */ + "getVarTagDependencies\0" /* 2424 */ + "statement\0" /* 2446 */ + "getOverridingThrowPoints\0" /* 2456 */ + "defaultExpr\0" /* 2481 */ + "PhpParser\\Node\\Expr\0" /* 2493 */ + "processStmtVarAnnotation\0" /* 2513 */ + "PHPStan\\PhpDoc\\Tag\\VarTag\0" /* 2538 */ + "findSingleVariableLessVarTag\0" /* 2564 */ + "emitVarTagChangedNode"; /* 2593 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 15), /* __construct $fileTypeMapper */ reg::packed(43, 0, 68), /* __construct $templateArgumentObserver */ @@ -201,20 +208,38 @@ inline constexpr reg::PackedArg args[] = { reg::packed(518, MAY_BE_CALLABLE), /* doProcessStmtNodes $nodeCallback */ reg::packed(632, 0, 640), /* doProcessStmtNodes $context */ reg::packed(531, 0, 903), /* doProcessStmtNodes return */ + reg::packed(357, 0, 375), /* processLoopPassStmtNodes $nodeScopeResolver */ + reg::packed(588, 0, 599), /* processLoopPassStmtNodes $parentNode */ + reg::packed(897, MAY_BE_ARRAY), /* processLoopPassStmtNodes $stmts */ + reg::packed(416, 0, 422), /* processLoopPassStmtNodes $scope */ + reg::packed(624, 0, 477), /* processLoopPassStmtNodes $storage */ + reg::packed(518, MAY_BE_CALLABLE), /* processLoopPassStmtNodes $nodeCallback */ + reg::packed(632, 0, 640), /* processLoopPassStmtNodes $context */ + reg::packed(963, MAY_BE_NULL | MAY_BE_ARRAY), /* processLoopPassStmtNodes $previous */ + reg::packed(972, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, true, false), /* processLoopPassStmtNodes $record */ + reg::packed(531, 0, 903), /* processLoopPassStmtNodes return */ + reg::packed(416, 0, 422), /* withByRefClosureHolders $scope */ + reg::packed(1004, MAY_BE_ARRAY), /* withByRefClosureHolders $variableNames */ + reg::packed(531, MAY_BE_ARRAY), /* withByRefClosureHolders return */ + reg::packed(1042, 0, 1048), /* rebaseAppendedPoints $state */ + reg::packed(1088, MAY_BE_LONG), /* rebaseAppendedPoints $exitPointCount */ + reg::packed(1103, MAY_BE_LONG), /* rebaseAppendedPoints $throwPointCount */ + reg::packed(1119, 0, 422), /* rebaseAppendedPoints $recordedEntry */ + reg::packed(531, MAY_BE_VOID), /* rebaseAppendedPoints return */ reg::packed(357, 0, 375), /* processStatementStep $nodeScopeResolver */ reg::packed(588, 0, 599), /* processStatementStep $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* processStatementStep $stmts */ - reg::packed(963, MAY_BE_LONG), /* processStatementStep $i */ - reg::packed(965, 0, 970), /* processStatementStep $stmt */ - reg::packed(990, 0, 996), /* processStatementStep $state */ + reg::packed(1154, MAY_BE_LONG), /* processStatementStep $i */ + reg::packed(1156, 0, 1161), /* processStatementStep $stmt */ + reg::packed(1042, 0, 1048), /* processStatementStep $state */ reg::packed(624, 0, 477), /* processStatementStep $storage */ reg::packed(518, MAY_BE_CALLABLE), /* processStatementStep $nodeCallback */ reg::packed(632, 0, 640), /* processStatementStep $context */ - reg::packed(1036, MAY_BE_BOOL), /* processStatementStep $shouldCheckLastStatement */ + reg::packed(1181, MAY_BE_BOOL), /* processStatementStep $shouldCheckLastStatement */ reg::packed(531, MAY_BE_VOID), /* processStatementStep return */ - reg::packed(965, 0, 970), /* readEndStatementExprResult $stmt */ + reg::packed(1156, 0, 1161), /* readEndStatementExprResult $stmt */ reg::packed(624, 0, 477), /* readEndStatementExprResult $storage */ - reg::packed(531, MAY_BE_NULL, 1082), /* readEndStatementExprResult return */ + reg::packed(531, MAY_BE_NULL, 1227), /* readEndStatementExprResult return */ reg::packed(357, 0, 375), /* processBodyStmtNodesTwoPass $nodeScopeResolver */ reg::packed(588, 0, 599), /* processBodyStmtNodesTwoPass $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* processBodyStmtNodesTwoPass $stmts */ @@ -226,124 +251,124 @@ inline constexpr reg::PackedArg args[] = { reg::packed(357, 0, 375), /* inferStaticVariableTypes $nodeScopeResolver */ reg::packed(588, 0, 599), /* inferStaticVariableTypes $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* inferStaticVariableTypes $stmts */ - reg::packed(1171, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticSites */ - reg::packed(1183, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticStatementIndexes */ - reg::packed(1206, MAY_BE_NULL, 1218), /* inferStaticVariableTypes $parentFrame */ - reg::packed(1266, MAY_BE_ARRAY), /* inferStaticVariableTypes $entries */ - reg::packed(1274, 0, 1284), /* inferStaticVariableTypes $recording */ + reg::packed(1316, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticSites */ + reg::packed(1328, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticStatementIndexes */ + reg::packed(1351, MAY_BE_NULL, 1363), /* inferStaticVariableTypes $parentFrame */ + reg::packed(1411, MAY_BE_ARRAY), /* inferStaticVariableTypes $entries */ + reg::packed(1419, 0, 1429), /* inferStaticVariableTypes $recording */ reg::packed(624, 0, 477), /* inferStaticVariableTypes $storage */ reg::packed(632, 0, 640), /* inferStaticVariableTypes $context */ reg::packed(531, MAY_BE_ARRAY), /* inferStaticVariableTypes return */ reg::packed(357, 0, 375), /* replayStaticVariableWalk $nodeScopeResolver */ reg::packed(897, MAY_BE_ARRAY), /* replayStaticVariableWalk $stmts */ - reg::packed(1266, MAY_BE_ARRAY), /* replayStaticVariableWalk $entries */ - reg::packed(1274, 0, 1284), /* replayStaticVariableWalk $recording */ - reg::packed(1348, MAY_BE_ARRAY), /* replayStaticVariableWalk $staticWalk */ - reg::packed(1359, 0, 1218), /* replayStaticVariableWalk $frame */ - reg::packed(1206, MAY_BE_NULL, 1218), /* replayStaticVariableWalk $parentFrame */ - reg::packed(1365, MAY_BE_NULL, 1383), /* replayStaticVariableWalk $parentConstraints */ + reg::packed(1411, MAY_BE_ARRAY), /* replayStaticVariableWalk $entries */ + reg::packed(1419, 0, 1429), /* replayStaticVariableWalk $recording */ + reg::packed(1493, MAY_BE_ARRAY), /* replayStaticVariableWalk $staticWalk */ + reg::packed(1504, 0, 1363), /* replayStaticVariableWalk $frame */ + reg::packed(1351, MAY_BE_NULL, 1363), /* replayStaticVariableWalk $parentFrame */ + reg::packed(1510, MAY_BE_NULL, 1528), /* replayStaticVariableWalk $parentConstraints */ reg::packed(624, 0, 477), /* replayStaticVariableWalk $storage */ reg::packed(518, MAY_BE_CALLABLE), /* replayStaticVariableWalk $nodeCallback */ reg::packed(416, 0, 422), /* replayStaticVariableWalk $scope */ reg::packed(531, 0, 903), /* replayStaticVariableWalk return */ - reg::packed(1462, 0, 422), /* collectStaticVariableScopes $bodyScope */ - reg::packed(1274, 0, 1284), /* collectStaticVariableScopes $recording */ - reg::packed(1472, 0, 996), /* collectStaticVariableScopes $endState */ - reg::packed(1481, MAY_BE_ARRAY), /* collectStaticVariableScopes $moreScopes */ + reg::packed(1607, 0, 422), /* collectStaticVariableScopes $bodyScope */ + reg::packed(1419, 0, 1429), /* collectStaticVariableScopes $recording */ + reg::packed(1617, 0, 1048), /* collectStaticVariableScopes $endState */ + reg::packed(1626, MAY_BE_ARRAY), /* collectStaticVariableScopes $moreScopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableScopes return */ - reg::packed(1462, 0, 422), /* collectStaticVariableStateScopes $bodyScope */ - reg::packed(1274, 0, 1284), /* collectStaticVariableStateScopes $recording */ - reg::packed(1472, 0, 996), /* collectStaticVariableStateScopes $endState */ - reg::packed(1481, MAY_BE_ARRAY), /* collectStaticVariableStateScopes $moreScopes */ + reg::packed(1607, 0, 422), /* collectStaticVariableStateScopes $bodyScope */ + reg::packed(1419, 0, 1429), /* collectStaticVariableStateScopes $recording */ + reg::packed(1617, 0, 1048), /* collectStaticVariableStateScopes $endState */ + reg::packed(1626, MAY_BE_ARRAY), /* collectStaticVariableStateScopes $moreScopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableStateScopes return */ - reg::packed(1553, MAY_BE_ARRAY), /* collectStaticVariableTypes $names */ - reg::packed(1559, MAY_BE_ARRAY), /* collectStaticVariableTypes $scopes */ + reg::packed(1698, MAY_BE_ARRAY), /* collectStaticVariableTypes $names */ + reg::packed(1704, MAY_BE_ARRAY), /* collectStaticVariableTypes $scopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableTypes return */ - reg::packed(1593, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $runs */ - reg::packed(1559, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $scopes */ + reg::packed(1738, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $runs */ + reg::packed(1704, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $scopes */ reg::packed(624, 0, 477), /* collectStaticVariableConditionalExpressions $storage */ - reg::packed(1598, MAY_BE_NULL | MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $types */ - reg::packed(1604, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $walkedConditionalExpressions */ - reg::packed(1633, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $enteredTypes */ + reg::packed(1743, MAY_BE_NULL | MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $types */ + reg::packed(1749, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $walkedConditionalExpressions */ + reg::packed(1778, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $enteredTypes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions return */ - reg::packed(990, MAY_BE_ARRAY), /* isWalkedStaticVariableState $state */ - reg::packed(1598, MAY_BE_ARRAY), /* isWalkedStaticVariableState $types */ + reg::packed(1042, MAY_BE_ARRAY), /* isWalkedStaticVariableState $state */ + reg::packed(1743, MAY_BE_ARRAY), /* isWalkedStaticVariableState $types */ reg::packed(416, 0, 422), /* isWalkedStaticVariableState $scope */ - reg::packed(1604, MAY_BE_ARRAY), /* isWalkedStaticVariableState $walkedConditionalExpressions */ + reg::packed(1749, MAY_BE_ARRAY), /* isWalkedStaticVariableState $walkedConditionalExpressions */ reg::packed(531, MAY_BE_BOOL), /* isWalkedStaticVariableState return */ - reg::packed(1718, MAY_BE_ARRAY), /* equalStaticVariableStates $a */ - reg::packed(1720, MAY_BE_ARRAY), /* equalStaticVariableStates $b */ + reg::packed(1863, MAY_BE_ARRAY), /* equalStaticVariableStates $a */ + reg::packed(1865, MAY_BE_ARRAY), /* equalStaticVariableStates $b */ reg::packed(531, MAY_BE_BOOL), /* equalStaticVariableStates return */ - reg::packed(1718, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $a */ - reg::packed(1720, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $b */ + reg::packed(1863, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $a */ + reg::packed(1865, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $b */ reg::packed(531, MAY_BE_BOOL), /* equalStaticVariableConditionalExpressions return */ - reg::packed(1718, 0, 1790), /* equalConditionalExpressionHolders $a */ - reg::packed(1720, 0, 1790), /* equalConditionalExpressionHolders $b */ + reg::packed(1863, 0, 1935), /* equalConditionalExpressionHolders $a */ + reg::packed(1865, 0, 1935), /* equalConditionalExpressionHolders $b */ reg::packed(531, MAY_BE_BOOL), /* equalConditionalExpressionHolders return */ - reg::packed(1462, 0, 422), /* joinStaticVariableTypes $bodyScope */ - reg::packed(1553, MAY_BE_ARRAY), /* joinStaticVariableTypes $names */ - reg::packed(1598, MAY_BE_ARRAY), /* joinStaticVariableTypes $types */ - reg::packed(1869, MAY_BE_ARRAY), /* joinStaticVariableTypes $walkTypes */ - reg::packed(1879, MAY_BE_BOOL), /* joinStaticVariableTypes $generalize */ + reg::packed(1607, 0, 422), /* joinStaticVariableTypes $bodyScope */ + reg::packed(1698, MAY_BE_ARRAY), /* joinStaticVariableTypes $names */ + reg::packed(1743, MAY_BE_ARRAY), /* joinStaticVariableTypes $types */ + reg::packed(2014, MAY_BE_ARRAY), /* joinStaticVariableTypes $walkTypes */ + reg::packed(2024, MAY_BE_BOOL), /* joinStaticVariableTypes $generalize */ reg::packed(531, MAY_BE_ARRAY), /* joinStaticVariableTypes return */ - reg::packed(531, 0, 1914), /* getClosureProcessor return */ + reg::packed(531, 0, 2059), /* getClosureProcessor return */ reg::packed(357, 0, 375), /* processDeferredByRefClosureBodies $nodeScopeResolver */ - reg::packed(1359, 0, 1218), /* processDeferredByRefClosureBodies $frame */ - reg::packed(990, 0, 996), /* processDeferredByRefClosureBodies $state */ + reg::packed(1504, 0, 1363), /* processDeferredByRefClosureBodies $frame */ + reg::packed(1042, 0, 1048), /* processDeferredByRefClosureBodies $state */ reg::packed(624, 0, 477), /* processDeferredByRefClosureBodies $storage */ reg::packed(518, MAY_BE_CALLABLE), /* processDeferredByRefClosureBodies $nodeCallback */ reg::packed(531, MAY_BE_VOID), /* processDeferredByRefClosureBodies return */ reg::packed(357, 0, 375), /* observeClosureSignatures $nodeScopeResolver */ reg::packed(588, 0, 599), /* observeClosureSignatures $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* observeClosureSignatures $stmts */ - reg::packed(1359, 0, 1218), /* observeClosureSignatures $frame */ - reg::packed(1266, MAY_BE_ARRAY), /* observeClosureSignatures $entries */ + reg::packed(1504, 0, 1363), /* observeClosureSignatures $frame */ + reg::packed(1411, MAY_BE_ARRAY), /* observeClosureSignatures $entries */ reg::packed(624, 0, 477), /* observeClosureSignatures $storage */ reg::packed(632, 0, 640), /* observeClosureSignatures $context */ - reg::packed(2002, MAY_BE_ARRAY), /* observeClosureSignatures $statementStartTokenPositions */ - reg::packed(531, 0, 1218), /* observeClosureSignatures return */ + reg::packed(2147, MAY_BE_ARRAY), /* observeClosureSignatures $statementStartTokenPositions */ + reg::packed(531, 0, 1363), /* observeClosureSignatures return */ reg::packed(416, 0, 422), /* withRecordedConstraints $scope */ - reg::packed(2056, 0, 422), /* withRecordedConstraints $recordedEntry */ - reg::packed(2070, 0, 422), /* withRecordedConstraints $recordedExit */ + reg::packed(1119, 0, 422), /* withRecordedConstraints $recordedEntry */ + reg::packed(2201, 0, 422), /* withRecordedConstraints $recordedExit */ reg::packed(531, 0, 422), /* withRecordedConstraints return */ reg::packed(897, MAY_BE_ARRAY), /* containsLabels $stmts */ reg::packed(531, MAY_BE_BOOL), /* containsLabels return */ - reg::packed(990, 0, 996), /* appendRecordedStatementResults $state */ - reg::packed(2122, 0, 996), /* appendRecordedStatementResults $from */ - reg::packed(2127, 0, 996), /* appendRecordedStatementResults $to */ + reg::packed(1042, 0, 1048), /* appendRecordedStatementResults $state */ + reg::packed(2253, 0, 1048), /* appendRecordedStatementResults $from */ + reg::packed(2258, 0, 1048), /* appendRecordedStatementResults $to */ reg::packed(531, MAY_BE_VOID), /* appendRecordedStatementResults return */ - reg::packed(965, 0, 970), /* getVariableMentionFlow $stmt */ - reg::packed(531, MAY_BE_NULL, 2161), /* getVariableMentionFlow return */ - reg::packed(965, 0, 970), /* statementMentionsAnyVariable $stmt */ - reg::packed(2214, MAY_BE_ARRAY), /* statementMentionsAnyVariable $variableNames */ + reg::packed(1156, 0, 1161), /* getVariableMentionFlow $stmt */ + reg::packed(531, MAY_BE_NULL, 2292), /* getVariableMentionFlow return */ + reg::packed(1156, 0, 1161), /* statementMentionsAnyVariable $stmt */ + reg::packed(1004, MAY_BE_ARRAY), /* statementMentionsAnyVariable $variableNames */ reg::packed(531, MAY_BE_BOOL), /* statementMentionsAnyVariable return */ - reg::packed(2257, 0, 599), /* collectMentionedVariables $node */ - reg::packed(1553, MAY_BE_ARRAY, reg::NoString, true, false), /* collectMentionedVariables $names */ - reg::packed(2262, MAY_BE_BOOL, reg::NoString, true, false), /* collectMentionedVariables $mentionsEverything */ + reg::packed(2374, 0, 599), /* collectMentionedVariables $node */ + reg::packed(1698, MAY_BE_ARRAY, reg::NoString, true, false), /* collectMentionedVariables $names */ + reg::packed(2379, MAY_BE_BOOL, reg::NoString, true, false), /* collectMentionedVariables $mentionsEverything */ reg::packed(531, MAY_BE_VOID), /* collectMentionedVariables return */ - reg::packed(965, 0, 970), /* getVarTagDependencies $stmt */ + reg::packed(1156, 0, 1161), /* getVarTagDependencies $stmt */ reg::packed(416, 0, 422), /* getVarTagDependencies $scope */ reg::packed(531, MAY_BE_NULL, 532), /* getVarTagDependencies return */ - reg::packed(2329, 0, 970), /* getOverridingThrowPoints $statement */ + reg::packed(2446, 0, 1161), /* getOverridingThrowPoints $statement */ reg::packed(416, 0, 422), /* getOverridingThrowPoints $scope */ reg::packed(531, MAY_BE_NULL | MAY_BE_ARRAY), /* getOverridingThrowPoints return */ reg::packed(357, 0, 375), /* processStmtVarAnnotation $nodeScopeResolver */ reg::packed(416, 0, 422), /* processStmtVarAnnotation $scope */ reg::packed(624, 0, 477), /* processStmtVarAnnotation $storage */ - reg::packed(965, 0, 970), /* processStmtVarAnnotation $stmt */ - reg::packed(2364, MAY_BE_NULL, 2376), /* processStmtVarAnnotation $defaultExpr */ + reg::packed(1156, 0, 1161), /* processStmtVarAnnotation $stmt */ + reg::packed(2481, MAY_BE_NULL, 2493), /* processStmtVarAnnotation $defaultExpr */ reg::packed(518, MAY_BE_CALLABLE), /* processStmtVarAnnotation $nodeCallback */ reg::packed(531, 0, 422), /* processStmtVarAnnotation return */ reg::packed(416, 0, 422), /* findSingleVariableLessVarTag $scope */ - reg::packed(965, 0, 970), /* findSingleVariableLessVarTag $stmt */ - reg::packed(531, MAY_BE_NULL, 2421), /* findSingleVariableLessVarTag return */ + reg::packed(1156, 0, 1161), /* findSingleVariableLessVarTag $stmt */ + reg::packed(531, MAY_BE_NULL, 2538), /* findSingleVariableLessVarTag return */ reg::packed(357, 0, 375), /* emitVarTagChangedNode $nodeScopeResolver */ reg::packed(416, 0, 422), /* emitVarTagChangedNode $scope */ reg::packed(624, 0, 477), /* emitVarTagChangedNode $storage */ - reg::packed(965, 0, 970), /* emitVarTagChangedNode $stmt */ - reg::packed(2364, 0, 2376), /* emitVarTagChangedNode $defaultExpr */ + reg::packed(1156, 0, 1161), /* emitVarTagChangedNode $stmt */ + reg::packed(2481, 0, 2493), /* emitVarTagChangedNode $defaultExpr */ reg::packed(518, MAY_BE_CALLABLE), /* emitVarTagChangedNode $nodeCallback */ - reg::packed(531, 0, 1383), /* emitVarTagChangedNode return */ + reg::packed(531, 0, 1528), /* emitVarTagChangedNode return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -357,34 +382,37 @@ inline constexpr sigtab::Sig mergeForwardGotoExitPoints = { { 790 /* mergeForwar inline constexpr sigtab::Sig processUnreachableStatement = { { 827 /* processUnreachableStatement */, 5, 26, 5, 31, ZEND_ACC_PRIVATE } }; inline constexpr sigtab::Sig getNextUnreachableStatements = { { 868 /* getNextUnreachableStatements */, 2, 32, 2, 34, ZEND_ACC_PRIVATE } }; inline constexpr sigtab::Sig doProcessStmtNodes = { { 944 /* doProcessStmtNodes */, 7, 35, 7, 42, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStatementStep = { { 1061 /* processStatementStep */, 10, 43, 10, 53, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig readEndStatementExprResult = { { 1116 /* readEndStatementExprResult */, 2, 54, 2, 56, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processBodyStmtNodesTwoPass = { { 1143 /* processBodyStmtNodesTwoPass */, 7, 57, 7, 64, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig inferStaticVariableTypes = { { 1323 /* inferStaticVariableTypes */, 10, 65, 10, 75, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig replayStaticVariableWalk = { { 1437 /* replayStaticVariableWalk */, 11, 76, 11, 87, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableScopes = { { 1492 /* collectStaticVariableScopes */, 4, 88, 4, 92, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableStateScopes = { { 1520 /* collectStaticVariableStateScopes */, 4, 93, 4, 97, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableTypes = { { 1566 /* collectStaticVariableTypes */, 2, 98, 2, 100, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableConditionalExpressions = { { 1646 /* collectStaticVariableConditionalExpressions */, 6, 101, 6, 107, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isWalkedStaticVariableState = { { 1690 /* isWalkedStaticVariableState */, 4, 108, 4, 112, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalStaticVariableStates = { { 1722 /* equalStaticVariableStates */, 2, 113, 2, 115, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalStaticVariableConditionalExpressions = { { 1748 /* equalStaticVariableConditionalExpressions */, 2, 116, 2, 118, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalConditionalExpressionHolders = { { 1835 /* equalConditionalExpressionHolders */, 2, 119, 2, 121, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig joinStaticVariableTypes = { { 1890 /* joinStaticVariableTypes */, 5, 122, 5, 127, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getClosureProcessor = { { 1948 /* getClosureProcessor */, 0, 128, 0, 128, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processDeferredByRefClosureBodies = { { 1968 /* processDeferredByRefClosureBodies */, 5, 129, 5, 134, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig observeClosureSignatures = { { 2031 /* observeClosureSignatures */, 8, 135, 8, 143, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig withRecordedConstraints = { { 2083 /* withRecordedConstraints */, 3, 144, 3, 147, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig containsLabels = { { 2107 /* containsLabels */, 1, 148, 1, 149, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig appendRecordedStatementResults = { { 2130 /* appendRecordedStatementResults */, 3, 150, 3, 153, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getVariableMentionFlow = { { 2191 /* getVariableMentionFlow */, 1, 154, 1, 155, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig statementMentionsAnyVariable = { { 2228 /* statementMentionsAnyVariable */, 2, 156, 2, 158, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectMentionedVariables = { { 2281 /* collectMentionedVariables */, 3, 159, 3, 162, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getVarTagDependencies = { { 2307 /* getVarTagDependencies */, 2, 163, 2, 165, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getOverridingThrowPoints = { { 2339 /* getOverridingThrowPoints */, 2, 166, 2, 168, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtVarAnnotation = { { 2396 /* processStmtVarAnnotation */, 6, 169, 6, 175, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findSingleVariableLessVarTag = { { 2447 /* findSingleVariableLessVarTag */, 2, 176, 2, 178, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig emitVarTagChangedNode = { { 2476 /* emitVarTagChangedNode */, 6, 179, 6, 185, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processLoopPassStmtNodes = { { 979 /* processLoopPassStmtNodes */, 9, 43, 9, 52, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withByRefClosureHolders = { { 1018 /* withByRefClosureHolders */, 2, 53, 2, 55, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig rebaseAppendedPoints = { { 1133 /* rebaseAppendedPoints */, 4, 56, 4, 60, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processStatementStep = { { 1206 /* processStatementStep */, 10, 61, 10, 71, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig readEndStatementExprResult = { { 1261 /* readEndStatementExprResult */, 2, 72, 2, 74, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processBodyStmtNodesTwoPass = { { 1288 /* processBodyStmtNodesTwoPass */, 7, 75, 7, 82, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig inferStaticVariableTypes = { { 1468 /* inferStaticVariableTypes */, 10, 83, 10, 93, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig replayStaticVariableWalk = { { 1582 /* replayStaticVariableWalk */, 11, 94, 11, 105, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableScopes = { { 1637 /* collectStaticVariableScopes */, 4, 106, 4, 110, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableStateScopes = { { 1665 /* collectStaticVariableStateScopes */, 4, 111, 4, 115, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableTypes = { { 1711 /* collectStaticVariableTypes */, 2, 116, 2, 118, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableConditionalExpressions = { { 1791 /* collectStaticVariableConditionalExpressions */, 6, 119, 6, 125, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isWalkedStaticVariableState = { { 1835 /* isWalkedStaticVariableState */, 4, 126, 4, 130, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalStaticVariableStates = { { 1867 /* equalStaticVariableStates */, 2, 131, 2, 133, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalStaticVariableConditionalExpressions = { { 1893 /* equalStaticVariableConditionalExpressions */, 2, 134, 2, 136, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalConditionalExpressionHolders = { { 1980 /* equalConditionalExpressionHolders */, 2, 137, 2, 139, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig joinStaticVariableTypes = { { 2035 /* joinStaticVariableTypes */, 5, 140, 5, 145, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getClosureProcessor = { { 2093 /* getClosureProcessor */, 0, 146, 0, 146, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processDeferredByRefClosureBodies = { { 2113 /* processDeferredByRefClosureBodies */, 5, 147, 5, 152, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeClosureSignatures = { { 2176 /* observeClosureSignatures */, 8, 153, 8, 161, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig withRecordedConstraints = { { 2214 /* withRecordedConstraints */, 3, 162, 3, 165, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig containsLabels = { { 2238 /* containsLabels */, 1, 166, 1, 167, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig appendRecordedStatementResults = { { 2261 /* appendRecordedStatementResults */, 3, 168, 3, 171, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getVariableMentionFlow = { { 2322 /* getVariableMentionFlow */, 1, 172, 1, 173, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig statementMentionsAnyVariable = { { 2345 /* statementMentionsAnyVariable */, 2, 174, 2, 176, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectMentionedVariables = { { 2398 /* collectMentionedVariables */, 3, 177, 3, 180, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getVarTagDependencies = { { 2424 /* getVarTagDependencies */, 2, 181, 2, 183, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOverridingThrowPoints = { { 2456 /* getOverridingThrowPoints */, 2, 184, 2, 186, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtVarAnnotation = { { 2513 /* processStmtVarAnnotation */, 6, 187, 6, 193, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findSingleVariableLessVarTag = { { 2564 /* findSingleVariableLessVarTag */, 2, 194, 2, 196, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig emitVarTagChangedNode = { { 2593 /* emitVarTagChangedNode */, 6, 197, 6, 203, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::StatementsHandler diff --git a/turbo-ext/src/support.h b/turbo-ext/src/support.h index 1090ddfb29c..128bc993851 100644 --- a/turbo-ext/src/support.h +++ b/turbo-ext/src/support.h @@ -2277,6 +2277,9 @@ extern zend_class_entry *pt_ce_node_scope_resolver; zv::Val pt_node_scope_resolver_process_expr_node(zval *nodeScopeResolver, zval *stmt, zval *expr, zval *scope, zval *storage, zval *nodeCallback, zval *context); zv::Val pt_node_scope_resolver_process_stmt_node(zval *nodeScopeResolver, zval *stmt, zval *scope, zval *storage, zval *nodeCallback, zval *context); zv::Val pt_node_scope_resolver_process_stmt_nodes_internal(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context); +/* $nodeScopeResolver->processLoopPassStmtNodes(...); previous IS_NULL for + * the first pass, record receives this pass's [entries, recording] */ +zv::Val pt_node_scope_resolver_process_loop_pass_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record); zv::Val pt_node_scope_resolver_process_expr_on_demand(zval *nodeScopeResolver, zval *expr, zval *scope, zval *storage); zv::Val pt_node_scope_resolver_process_synthetic_on_demand(zval *nodeScopeResolver, zval *expr, zval *scope); zv::Val pt_node_scope_resolver_find_scope_state_type(zval *nodeScopeResolver, zval *expr, zval *scope); @@ -2303,6 +2306,10 @@ zv::Val pt_statements_handler_process_nodes_with_storage(zval *handler, zval *no * null, UNDEF = pending exception */ zv::Val pt_statements_handler_get_var_tag_dependencies(zval *handler, zval *stmt, zval *scope); zv::Val pt_statements_handler_do_process_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context); +/* $handler->processLoopPassStmtNodes(...): one loop convergence pass re-walking + * only what differs from $previous (IS_NULL for the first pass); record + * receives this pass's [entries, recording]. UNDEF = pending exception */ +zv::Val pt_statements_handler_process_loop_pass_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record); zv::Val pt_statements_handler_process_stmt_var_annotation(zval *handler, zval *nodeScopeResolver, zval *scope, zval *storage, zval *stmt, zval *defaultExpr, zval *nodeCallback); zv::Val pt_statements_handler_get_overriding_throw_points(zval *handler, zval *statement, zval *scope); zv::Val pt_statements_handler_emit_var_tag_changed_node(zval *handler, zval *nodeScopeResolver, zval *scope, zval *storage, zval *stmt, zval *defaultExpr, zval *nodeCallback); From 840e9e26258c0f3844043d112e8cce015873c870 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:10 +0200 Subject: [PATCH 2/8] Re-walk only changed statements in later while, do-while and for passes The convergence loops of while, do-while and for now go through processLoopPassStmtNodes() like foreach: a later pass replays the statements whose inputs did not change since the previous pass. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- src/Analyser/StmtHandler/DoWhileHandler.php | 13 ++++++++++++- src/Analyser/StmtHandler/ForHandler.php | 13 ++++++++++++- src/Analyser/StmtHandler/WhileHandler.php | 13 ++++++++++++- turbo-ext/src/DoWhileHandler.cpp | 11 ++++++++++- turbo-ext/src/ForHandler.cpp | 11 ++++++++++- turbo-ext/src/WhileHandler.cpp | 11 ++++++++++- 6 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/Analyser/StmtHandler/DoWhileHandler.php b/src/Analyser/StmtHandler/DoWhileHandler.php index 31389cb599f..4e5a8a04791 100644 --- a/src/Analyser/StmtHandler/DoWhileHandler.php +++ b/src/Analyser/StmtHandler/DoWhileHandler.php @@ -67,6 +67,10 @@ public function processStmt( $prevEntryScope = null; if ($context->isTopLevel()) { $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + $previousPass = null; + $previousPassStorage = null; do { $prevScope = $bodyScope; $bodyScope = $bodyScope->mergeWith($scope); @@ -79,10 +83,17 @@ public function processStmt( } $prevEntryScope = $bodyScope; $storage = $originalStorage->duplicate(); + if ($previousPassStorage !== null) { + // the statements this pass replays answer from the results the previous pass stored + $storage->mergeResults($previousPassStorage); + } $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); $scope->pushExpressionResultStorage($storage); try { - $bodyScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution())->filterOutLoopExitPoints(); + $passRecord = null; + $bodyScopeResult = $nodeScopeResolver->processLoopPassStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution(), $previousPass, $passRecord)->filterOutLoopExitPoints(); + $previousPass = $passRecord; + $previousPassStorage = $storage; $backEdgeScope = $bodyScopeResult->getLoopBackEdgeScope(); $finalScope = $backEdgeScope === null ? $finalScope : $backEdgeScope->mergeWith($finalScope); foreach ($bodyScopeResult->getExitPointsByType(Break_::class) as $breakExitPoint) { diff --git a/src/Analyser/StmtHandler/ForHandler.php b/src/Analyser/StmtHandler/ForHandler.php index e59ec2e5d15..7a95e2c5710 100644 --- a/src/Analyser/StmtHandler/ForHandler.php +++ b/src/Analyser/StmtHandler/ForHandler.php @@ -198,6 +198,10 @@ public function processStmt( if ($context->isTopLevel()) { $count = 0; $prevEntryScope = null; + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + $previousPass = null; + $previousPassStorage = null; do { $prevScope = $bodyScope; $storage = $originalStorage->duplicate(); @@ -209,6 +213,10 @@ public function processStmt( break; } $prevEntryScope = $bodyScope; + if ($previousPassStorage !== null) { + // the statements this pass replays answer from the results the previous pass stored + $storage->mergeResults($previousPassStorage); + } $scope->pushExpressionResultStorage($storage); try { $passFlows = []; @@ -217,7 +225,10 @@ public function processStmt( $bodyScope = $passCondResult->getTruthyScope(); $passFlows[] = $passCondResult->getVariableFlow(); } - $bodyScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, new NoopNodeCallback(), $context->enterDeep()->withoutTemplateArgumentResolution())->filterOutLoopExitPoints(); + $passRecord = null; + $bodyScopeResult = $nodeScopeResolver->processLoopPassStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, new NoopNodeCallback(), $context->enterDeep()->withoutTemplateArgumentResolution(), $previousPass, $passRecord)->filterOutLoopExitPoints(); + $previousPass = $passRecord; + $previousPassStorage = $storage; $passFlows[] = $bodyScopeResult->getVariableFlow(); $backEdgeScope = $bodyScopeResult->getLoopBackEdgeScope(); if ($backEdgeScope === null) { diff --git a/src/Analyser/StmtHandler/WhileHandler.php b/src/Analyser/StmtHandler/WhileHandler.php index 02b00e0fa37..7b348c33d48 100644 --- a/src/Analyser/StmtHandler/WhileHandler.php +++ b/src/Analyser/StmtHandler/WhileHandler.php @@ -95,6 +95,10 @@ public function processStmt( if ($context->isTopLevel()) { $count = 0; $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + $previousPass = null; + $previousPassStorage = null; do { $prevScope = $bodyScope; $bodyScope = $bodyScope->mergeWith($scope); @@ -106,13 +110,20 @@ public function processStmt( } $prevEntryScope = $bodyScope; $storage = $originalStorage->duplicate(); + if ($previousPassStorage !== null) { + // the statements this pass replays answer from the results the previous pass stored + $storage->mergeResults($previousPassStorage); + } $condRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); $scope->pushExpressionResultStorage($storage); try { $passCondResult = $nodeScopeResolver->processExprNode($stmt, $stmt->cond, $bodyScope, $storage, $condRecording, ExpressionContext::createDeep(resolveTemplateArguments: false)); $bodyScope = $passCondResult->getTruthyScope(); - $bodyScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution())->filterOutLoopExitPoints(); + $passRecord = null; + $bodyScopeResult = $nodeScopeResolver->processLoopPassStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $bodyRecording, $context->enterDeep()->withoutTemplateArgumentResolution(), $previousPass, $passRecord)->filterOutLoopExitPoints(); + $previousPass = $passRecord; + $previousPassStorage = $storage; $backEdgeScope = $bodyScopeResult->getLoopBackEdgeScope(); } finally { $scope->popExpressionResultStorage(); diff --git a/turbo-ext/src/DoWhileHandler.cpp b/turbo-ext/src/DoWhileHandler.cpp index 3a66b20a14b..6da5a080bdb 100644 --- a/turbo-ext/src/DoWhileHandler.cpp +++ b/turbo-ext/src/DoWhileHandler.cpp @@ -75,6 +75,10 @@ class DoWhileHandler if (UNEXPECTED(stmts == NULL)) return zv::Val(); bool bodyIsReplayable; if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, stmts, bodyIsReplayable))) return zv::Val(); + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); do { zv::Val prevScope = zv::Val::copyOf(bodyScope.ref()); bodyScope = pt_mutating_scope_merge_with(Z_OBJ_P(bodyScope.raw()), scope); @@ -93,6 +97,8 @@ class DoWhileHandler prevEntryScope = zv::Val::copyOf(bodyScope.ref()); zv::Val storage = pt_expression_result_storage_duplicate(originalStorage); if (UNEXPECTED(storage.isUndef())) return zv::Val(); + // the statements this pass replays answer from the results the previous pass stored + if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); if (UNEXPECTED(bodyRecording.isUndef())) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope), storage.raw()))) return zv::Val(); @@ -106,8 +112,11 @@ class DoWhileHandler if (UNEXPECTED(deepContext.isUndef())) return; zv::Val passContext = pt_statement_context_without_template_argument_resolution(deepContext.raw()); if (UNEXPECTED(passContext.isUndef())) return; - zv::Val walked = pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw()); + zv::Val passRecord = zv::Val::null(); + zv::Val walked = pt_node_scope_resolver_process_loop_pass_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw(), previousPass.raw(), passRecord); if (UNEXPECTED(walked.isUndef())) return; + previousPass = std::move(passRecord); + previousPassStorage = zv::Val::copyOf(storage.ref()); bodyScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); if (UNEXPECTED(bodyScopeResult.isUndef())) return; zv::Val backEdgeScope = pt_internal_statement_result_loop_back_edge_scope(bodyScopeResult.raw()); diff --git a/turbo-ext/src/ForHandler.cpp b/turbo-ext/src/ForHandler.cpp index 0cd0f205138..f187def3240 100644 --- a/turbo-ext/src/ForHandler.cpp +++ b/turbo-ext/src/ForHandler.cpp @@ -215,6 +215,10 @@ class ForHandler if (isTopLevel) { zend_long count = 0; zv::Val prevEntryScope; + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); do { zv::Val prevScope = zv::Val::copyOf(bodyScope.ref()); zv::Val storage = pt_expression_result_storage_duplicate(originalStorage); @@ -232,6 +236,8 @@ class ForHandler } } prevEntryScope = zv::Val::copyOf(bodyScope.ref()); + // the statements this pass replays answer from the results the previous pass stored + if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope), storage.raw()))) return zv::Val(); zv::Arr passFlows = zv::Arr::empty(); bool brokeOut = false; @@ -258,8 +264,11 @@ class ForHandler if (UNEXPECTED(deepContext.isUndef())) return; zv::Val passContext = pt_statement_context_without_template_argument_resolution(deepContext.raw()); if (UNEXPECTED(passContext.isUndef())) return; - zv::Val walked = pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), noop.raw(), passContext.raw()); + zv::Val passRecord = zv::Val::null(); + zv::Val walked = pt_node_scope_resolver_process_loop_pass_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), noop.raw(), passContext.raw(), previousPass.raw(), passRecord); if (UNEXPECTED(walked.isUndef())) return; + previousPass = std::move(passRecord); + previousPassStorage = zv::Val::copyOf(storage.ref()); zv::Val bodyScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); if (UNEXPECTED(bodyScopeResult.isUndef())) return; { diff --git a/turbo-ext/src/WhileHandler.cpp b/turbo-ext/src/WhileHandler.cpp index 6a1eba5fda3..1cb1dfa442d 100644 --- a/turbo-ext/src/WhileHandler.cpp +++ b/turbo-ext/src/WhileHandler.cpp @@ -129,6 +129,10 @@ class WhileHandler if (UNEXPECTED(stmts == NULL)) return zv::Val(); bool bodyIsReplayable; if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, stmts, bodyIsReplayable))) return zv::Val(); + // each pass after the first re-walks only the statements whose + // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); do { zv::Val prevScope = zv::Val::copyOf(bodyScope.ref()); bodyScope = pt_mutating_scope_merge_with(Z_OBJ_P(bodyScope.raw()), scope.raw()); @@ -146,6 +150,8 @@ class WhileHandler prevEntryScope = zv::Val::copyOf(bodyScope.ref()); storage = pt_expression_result_storage_duplicate(originalStorage); if (UNEXPECTED(storage.isUndef())) return zv::Val(); + // the statements this pass replays answer from the results the previous pass stored + if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); zv::Val condRecording = ptlh::newPassNodeCallback(bodyIsReplayable); if (UNEXPECTED(condRecording.isUndef())) return zv::Val(); zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); @@ -170,8 +176,11 @@ class WhileHandler if (UNEXPECTED(deepContext.isUndef())) return; zv::Val passContext = pt_statement_context_without_template_argument_resolution(deepContext.raw()); if (UNEXPECTED(passContext.isUndef())) return; - zv::Val walked = pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw()); + zv::Val passRecord = zv::Val::null(); + zv::Val walked = pt_node_scope_resolver_process_loop_pass_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage.raw(), bodyRecording.raw(), passContext.raw(), previousPass.raw(), passRecord); if (UNEXPECTED(walked.isUndef())) return; + previousPass = std::move(passRecord); + previousPassStorage = zv::Val::copyOf(storage.ref()); bodyScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); if (UNEXPECTED(bodyScopeResult.isUndef())) return; backEdgeScope = pt_internal_statement_result_loop_back_edge_scope(bodyScopeResult.raw()); From f1fc42734388dc8b69be31008641dee42e4c52f0 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:10 +0200 Subject: [PATCH 3/8] Replay the statements a loop's last pass stands in for in its final walk A loop body holding a construct that walks differently at top level than in a convergence pass (a nested loop runs its own convergence only in the final walk) was walked in full by the final walk, although only those statements differ. The passes of such bodies now record their emissions too, and the final walk runs against the last pass like a later pass does: a statement mentioning no variable whose state differs from the pass's entry replays the pass's emissions through the real callback, and statements holding such a construct are walked. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- src/Analyser/NodeScopeResolver.php | 47 +++ src/Analyser/StatementsHandler.php | 106 +++++- src/Analyser/StmtHandler/DoWhileHandler.php | 13 +- src/Analyser/StmtHandler/ForeachHandler.php | 13 +- src/Analyser/StmtHandler/WhileHandler.php | 21 +- turbo-ext/src/DoWhileHandler.cpp | 26 +- turbo-ext/src/ForeachHandler.cpp | 27 +- turbo-ext/src/NodeScopeResolver.cpp | 87 +++++ turbo-ext/src/StatementsHandler.cpp | 97 ++++- turbo-ext/src/WhileHandler.cpp | 24 +- turbo-ext/src/generated/NodeScopeResolver.h | 315 +++++++-------- turbo-ext/src/generated/StatementsHandler.h | 401 +++++++++++--------- turbo-ext/src/support.h | 3 + 13 files changed, 788 insertions(+), 392 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index b2dfcb07d4c..6d43f10e951 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -286,6 +286,37 @@ public function processStmtNodes( } } + /** + * The final walk of a loop body against its last convergence pass (see + * StatementsHandler::processLoopFinalStmtNodes()). + * + * @param Node\Stmt[] $stmts + * @param callable(Node $node, Scope $scope): void $nodeCallback + * @param array{array, RecordingNodeCallback} $lastPass + */ + public function processLoopFinalStmtNodes( + Node $parentNode, + array $stmts, + MutatingScope $scope, + ExpressionResultStorage $storage, + callable $nodeCallback, + StatementContext $context, + array $lastPass, + ): InternalStatementResult + { + $pushStorage = $scope->getCurrentExpressionResultStorage() !== $storage; + if ($pushStorage) { + $scope->pushExpressionResultStorage($storage); + } + try { + return $this->statementsHandler->processLoopFinalStmtNodes($this, $parentNode, $stmts, $scope, $storage, $nodeCallback, $context, $lastPass); + } finally { + if ($pushStorage) { + $scope->popExpressionResultStorage(); + } + } + } + /** * One loop convergence pass over $stmts, re-walking only what differs from * $previous, the previous pass (see StatementsHandler::processLoopPassStmtNodes()). @@ -962,6 +993,8 @@ public function getAssignedVariables(Expr $expr): array private const REPLAYABLE_BODY_ATTRIBUTE = 'convergenceReplayableBody'; + private const CONTEXT_SENSITIVE_STATEMENT_ATTRIBUTE = 'convergenceContextSensitiveStatement'; + /** * Whether a recorded convergence pass over the loop body can replace the * final walk. A pass runs at deep statement context, the final walk at top @@ -991,6 +1024,20 @@ public function isReplayableConvergenceBody(Node $loopNode, array $bodyStmts): b return $replayable; } + /** A statement that walks differently in a convergence pass than in the final walk. */ + public function isContextSensitiveStatement(Node\Stmt $stmt): bool + { + $cached = $stmt->getAttribute(self::CONTEXT_SENSITIVE_STATEMENT_ATTRIBUTE); + if ($cached !== null) { + return $cached; + } + + $sensitive = $this->hasContextSensitiveConstruct($stmt); + $stmt->setAttribute(self::CONTEXT_SENSITIVE_STATEMENT_ATTRIBUTE, $sensitive); + + return $sensitive; + } + private function hasContextSensitiveConstruct(Node $node): bool { if ($node instanceof Expr\Closure) { diff --git a/src/Analyser/StatementsHandler.php b/src/Analyser/StatementsHandler.php index 9140085de4f..aae13558898 100644 --- a/src/Analyser/StatementsHandler.php +++ b/src/Analyser/StatementsHandler.php @@ -428,42 +428,115 @@ public function processLoopPassStmtNodes( ): InternalStatementResult { $recording = $nodeCallback instanceof RecordingNodeCallback ? $nodeCallback : null; + [$result, $entries] = $this->walkAgainstPreviousPass($nodeScopeResolver, $parentNode, $stmts, $scope, $storage, $nodeCallback, $context, $previous, $recording, false); + $record = [$entries, $recording]; + + return $result; + } + + /** + * The final walk of a loop body that the last convergence pass could not + * stand in for as a whole: like a later pass against $lastPass, with the + * replayed emissions going to the real callback, and every statement + * holding a construct that walks differently outside a pass (a nested + * loop runs its own convergence only here) walked. + * + * @param Node\Stmt[] $stmts + * @param callable(Node $node, Scope $scope): void $nodeCallback + * @param array{array, RecordingNodeCallback} $lastPass + */ + public function processLoopFinalStmtNodes( + NodeScopeResolver $nodeScopeResolver, + Node $parentNode, + array $stmts, + MutatingScope $scope, + ExpressionResultStorage $storage, + callable $nodeCallback, + StatementContext $context, + array $lastPass, + ): InternalStatementResult + { + return $this->walkAgainstPreviousPass($nodeScopeResolver, $parentNode, $stmts, $scope, $storage, $nodeCallback, $context, $lastPass, $nodeCallback, true)[0]; + } + + /** + * Against the previous pass's per-statement entries, a statement is + * re-walked only when its entry state differs in a key spelling no + * variable, or it mentions a variable whose state differs; otherwise its + * recorded emissions are replayed into $replayTarget (none without one) + * and its recorded effect is carried onto the current scope. Once the + * entry state equals the previous pass's, the rest of the previous pass + * stands. A final walk records no entries. + * + * @param Node\Stmt[] $stmts + * @param callable(Node $node, Scope $scope): void $nodeCallback + * @param array{array, ?RecordingNodeCallback}|null $previous + * @param (callable(Node $node, Scope $scope): void)|null $replayTarget + * @return array{InternalStatementResult, array} + */ + private function walkAgainstPreviousPass( + NodeScopeResolver $nodeScopeResolver, + Node $parentNode, + array $stmts, + MutatingScope $scope, + ExpressionResultStorage $storage, + callable $nodeCallback, + StatementContext $context, + ?array $previous, + ?callable $replayTarget, + bool $finalWalk, + ): array + { + $recording = !$finalWalk && $nodeCallback instanceof RecordingNodeCallback ? $nodeCallback : null; $previousRecording = $previous !== null ? $previous[1] : null; // a replayed statement's emissions come from the previous recording; // labels make the statement order no straight line $canReplay = $previous !== null - && ($recording === null || $previousRecording !== null) + && ($replayTarget === null || $previousRecording !== null) && !$this->containsLabels($stmts); $stmtCount = count($stmts); + // the last statement a final walk walks whatever the state + $lastWalked = -1; + if ($canReplay && $finalWalk) { + for ($i = $stmtCount - 1; $i >= 0; $i--) { + if ($nodeScopeResolver->isContextSensitiveStatement($stmts[$i])) { + $lastWalked = $i; + break; + } + } + } $state = new StatementListWalkState($scope); $entries = []; for ($i = 0; $i < $stmtCount; $i++) { - $entries[$i] = [clone $state, $recording !== null ? $recording->count() : 0]; - if ($canReplay) { + if (!$finalWalk) { + $entries[$i] = [clone $state, $recording !== null ? $recording->count() : 0]; + } + if ($canReplay && ($i > $lastWalked || !$nodeScopeResolver->isContextSensitiveStatement($stmts[$i]))) { [$recordedEntry, $offset] = $previous[0][$i]; [$recordedExit, $nextOffset] = $previous[0][$i + 1]; $differingRoots = $state->alreadyTerminated === $recordedEntry->alreadyTerminated ? $state->scope->getDifferingVariableRoots($recordedEntry->scope) : null; - if ($differingRoots === []) { + if ($differingRoots === [] && $i > $lastWalked) { // converged with the previous pass: the rest of it stands [$recordedEnd, $endOffset] = $previous[0][$stmtCount]; - if ($recording !== null && $previousRecording !== null) { - $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $endOffset, $recording, $storage, $scope); + if ($replayTarget !== null && $previousRecording !== null) { + $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $endOffset, $replayTarget, $storage, $scope); } $this->appendRecordedStatementResults($state, $recordedEntry, $recordedEnd); $state->scope = $recordedEnd->scope; - $base = $entries[$i][1]; - for ($j = $i; $j <= $stmtCount; $j++) { - $entries[$j] = [$previous[0][$j][0], $base + $previous[0][$j][1] - $offset]; + if (!$finalWalk) { + $base = $entries[$i][1]; + for ($j = $i; $j <= $stmtCount; $j++) { + $entries[$j] = [$previous[0][$j][0], $base + $previous[0][$j][1] - $offset]; + } } - $record = [$entries, $recording]; - return $state->toResult(); + return [$state->toResult(), $entries]; } if ($differingRoots !== null && !$this->statementMentionsAnyVariable($stmts[$i], $this->withByRefClosureHolders($state->scope, $differingRoots))) { - if ($recording !== null && $previousRecording !== null) { - $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $nextOffset, $recording, $storage, $scope); + if ($replayTarget !== null && $previousRecording !== null) { + $nodeScopeResolver->replayRecordingRange($previousRecording, $offset, $nextOffset, $replayTarget, $storage, $scope); } $exitPointCount = count($state->exitPoints); $throwPointCount = count($state->throwPoints); @@ -475,10 +548,11 @@ public function processLoopPassStmtNodes( } $this->processStatementStep($nodeScopeResolver, $parentNode, $stmts, $i, $stmts[$i], $state, $storage, $nodeCallback, $context, false); } - $entries[$stmtCount] = [clone $state, $recording !== null ? $recording->count() : 0]; - $record = [$entries, $recording]; + if (!$finalWalk) { + $entries[$stmtCount] = [clone $state, $recording !== null ? $recording->count() : 0]; + } - return $state->toResult(); + return [$state->toResult(), $entries]; } /** diff --git a/src/Analyser/StmtHandler/DoWhileHandler.php b/src/Analyser/StmtHandler/DoWhileHandler.php index 4e5a8a04791..f05e8967912 100644 --- a/src/Analyser/StmtHandler/DoWhileHandler.php +++ b/src/Analyser/StmtHandler/DoWhileHandler.php @@ -65,12 +65,12 @@ public function processStmt( $replayPassStorage = null; $replayPassResult = null; $prevEntryScope = null; + $previousPass = null; + $previousPassStorage = null; if ($context->isTopLevel()) { $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); // each pass after the first re-walks only the statements whose // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - $previousPass = null; - $previousPassStorage = null; do { $prevScope = $bodyScope; $bodyScope = $bodyScope->mergeWith($scope); @@ -87,7 +87,7 @@ public function processStmt( // the statements this pass replays answer from the results the previous pass stored $storage->mergeResults($previousPassStorage); } - $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); + $bodyRecording = new RecordingNodeCallback(); $scope->pushExpressionResultStorage($storage); try { $passRecord = null; @@ -101,7 +101,7 @@ public function processStmt( } // the candidate to replace the final body walk when this pass's // entry turns out to be the fixpoint - if ($bodyRecording instanceof RecordingNodeCallback) { + if ($bodyIsReplayable) { $replayBodyRecording = $bodyRecording; $replayPassStorage = $storage; $replayPassResult = $bodyScopeResult; @@ -143,6 +143,11 @@ public function processStmt( $originalStorage->mergeResults($replayPassStorage); $nodeScopeResolver->replayRecording($replayBodyRecording, $nodeCallback, $originalStorage, $scope); $bodyScopeResult = $replayPassResult; + } elseif ($previousPass !== null && $previousPass[1] !== null) { + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + $originalStorage->mergeResults($previousPassStorage); + $bodyScopeResult = $nodeScopeResolver->processLoopFinalStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context, $previousPass)->filterOutLoopExitPoints(); } else { $bodyScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context)->filterOutLoopExitPoints(); } diff --git a/src/Analyser/StmtHandler/ForeachHandler.php b/src/Analyser/StmtHandler/ForeachHandler.php index 0c264a91360..afdc558537c 100644 --- a/src/Analyser/StmtHandler/ForeachHandler.php +++ b/src/Analyser/StmtHandler/ForeachHandler.php @@ -188,6 +188,8 @@ static function () use ($condResult, $emptyArrayType): Type { } $originalStorage = $storage; + $previousPass = null; + $previousPassStorage = null; $replayBodyRecording = null; $replayPassStorage = null; $replayPassResult = null; @@ -228,8 +230,6 @@ static function () use ($condResult, $emptyArrayType): Type { $prevEntryScope = null; // each pass after the first re-walks only the statements whose // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - $previousPass = null; - $previousPassStorage = null; $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); do { $prevScope = $bodyScope; @@ -246,7 +246,7 @@ static function () use ($condResult, $emptyArrayType): Type { // the statements this pass replays answer from the results the previous pass stored $storage->mergeResults($previousPassStorage); } - $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); + $bodyRecording = new RecordingNodeCallback(); $scope->pushExpressionResultStorage($storage); try { $bodyScope = $this->enterForeach($nodeScopeResolver, $bodyScope, $storage, $originalScope, $stmt, $foreachIterateeType, $foreachNativeIterateeType, $nodeCallback); @@ -265,7 +265,7 @@ static function () use ($condResult, $emptyArrayType): Type { } // the candidate to replace the final walk when this pass's // entry turns out to be the fixpoint - if ($bodyRecording instanceof RecordingNodeCallback) { + if ($bodyIsReplayable) { $replayBodyRecording = $bodyRecording; $replayPassStorage = $storage; $replayPassResult = $bodyScopeResult; @@ -298,6 +298,11 @@ static function () use ($condResult, $emptyArrayType): Type { $originalStorage->mergeResults($replayPassStorage); $nodeScopeResolver->replayRecording($replayBodyRecording, $nodeCallback, $originalStorage, $scope); $finalScopeResult = $replayPassResult; + } elseif ($previousPass !== null && $previousPass[1] !== null) { + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + $originalStorage->mergeResults($previousPassStorage); + $finalScopeResult = $nodeScopeResolver->processLoopFinalStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context, $previousPass)->filterOutLoopExitPoints(); } else { $finalPassContext = $unrolledTotalKeys !== null ? $context->enterUnrolledForeach($unrolledTotalKeys) : $context; $finalScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $finalPassContext)->filterOutLoopExitPoints(); diff --git a/src/Analyser/StmtHandler/WhileHandler.php b/src/Analyser/StmtHandler/WhileHandler.php index 7b348c33d48..aa198450901 100644 --- a/src/Analyser/StmtHandler/WhileHandler.php +++ b/src/Analyser/StmtHandler/WhileHandler.php @@ -92,13 +92,11 @@ public function processStmt( $replayPassStorage = null; $replayPassResult = null; $prevEntryScope = null; + $previousPass = null; + $previousPassStorage = null; if ($context->isTopLevel()) { $count = 0; $bodyIsReplayable = $nodeScopeResolver->isReplayableConvergenceBody($stmt, $stmt->stmts); - // each pass after the first re-walks only the statements whose - // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - $previousPass = null; - $previousPassStorage = null; do { $prevScope = $bodyScope; $bodyScope = $bodyScope->mergeWith($scope); @@ -115,7 +113,7 @@ public function processStmt( $storage->mergeResults($previousPassStorage); } $condRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); - $bodyRecording = $bodyIsReplayable ? new RecordingNodeCallback() : new NoopNodeCallback(); + $bodyRecording = new RecordingNodeCallback(); $scope->pushExpressionResultStorage($storage); try { $passCondResult = $nodeScopeResolver->processExprNode($stmt, $stmt->cond, $bodyScope, $storage, $condRecording, ExpressionContext::createDeep(resolveTemplateArguments: false)); @@ -177,12 +175,23 @@ public function processStmt( $bodyCondResult = $replayCondResult; $finalScopeResult = $replayPassResult; } else { + $lastPass = $previousPass !== null && $previousPass[1] !== null ? $previousPass : null; + if ($lastPass !== null) { + // before the condition's real walk stores its own results + $originalStorage->mergeResults($previousPassStorage); + } $bodyCondResult = $nodeScopeResolver->processExprNode($stmt, $stmt->cond, $bodyScope, $storage, $nodeCallback, ExpressionContext::createDeep($context->shouldResolveTemplateArguments())); // the While_ callback is deferred from processStmtNode(): it fires after // the condition's real walk stored its result, with the entry scope $nodeScopeResolver->callNodeCallback($nodeCallback, $stmt, $scope, $storage); $bodyScope = $bodyCondResult->getTruthyScope(); - $finalScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context)->filterOutLoopExitPoints(); + if ($lastPass !== null) { + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + $finalScopeResult = $nodeScopeResolver->processLoopFinalStmtNodes($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context, $lastPass)->filterOutLoopExitPoints(); + } else { + $finalScopeResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $bodyScope, $storage, $nodeCallback, $context)->filterOutLoopExitPoints(); + } } $backEdgeScope = $finalScopeResult->getLoopBackEdgeScope(); $backEdgeDead = $backEdgeScope === null; diff --git a/turbo-ext/src/DoWhileHandler.cpp b/turbo-ext/src/DoWhileHandler.cpp index 6da5a080bdb..8bd3377df21 100644 --- a/turbo-ext/src/DoWhileHandler.cpp +++ b/turbo-ext/src/DoWhileHandler.cpp @@ -35,6 +35,15 @@ pt_property_site pt_dwh_stmts_site; } // namespace +/* the last convergence pass when it recorded its emissions (its + * [entries, recording] record), NULL otherwise */ +static zval *lastRecordedPass(zv::Val &previousPass) +{ + if (!previousPass.ref().isArray()) return NULL; + zval *recording = zend_hash_index_find(Z_ARRVAL_P(previousPass.raw()), 1); + return recording != NULL && Z_TYPE_P(recording) == IS_OBJECT ? previousPass.raw() : NULL; +} + namespace phpstanturbo { /* Mirrors PHPStan\Analyser\StmtHandler\DoWhileHandler; UNDEF = pending @@ -70,6 +79,8 @@ class DoWhileHandler zv::Val replayBodyRecording, replayPassStorage, replayPassResult; zv::Val prevEntryScope; + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); if (isTopLevel) { zval *stmts = ptsh::readNodeProperty(pt_dwh_stmts_site, stmt, PT_LC("stmts")); if (UNEXPECTED(stmts == NULL)) return zv::Val(); @@ -77,8 +88,6 @@ class DoWhileHandler if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, stmts, bodyIsReplayable))) return zv::Val(); // each pass after the first re-walks only the statements whose // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - zv::Val previousPass = zv::Val::null(); - zv::Val previousPassStorage = zv::Val::null(); do { zv::Val prevScope = zv::Val::copyOf(bodyScope.ref()); bodyScope = pt_mutating_scope_merge_with(Z_OBJ_P(bodyScope.raw()), scope); @@ -99,7 +108,7 @@ class DoWhileHandler if (UNEXPECTED(storage.isUndef())) return zv::Val(); // the statements this pass replays answer from the results the previous pass stored if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); - zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); + zv::Val bodyRecording = ptlh::newPassNodeCallback(true); if (UNEXPECTED(bodyRecording.isUndef())) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope), storage.raw()))) return zv::Val(); zv::Val bodyScopeResult, passCondResult; @@ -197,6 +206,17 @@ class DoWhileHandler if (UNEXPECTED(!pt_expression_result_storage_merge_results(originalStorage, replayPassStorage.raw()))) return zv::Val(); if (UNEXPECTED(!pt_node_scope_resolver_replay_recording(nodeScopeResolver, replayBodyRecording.raw(), nodeCallback, originalStorage, scope))) return zv::Val(); bodyScopeResult = std::move(replayPassResult); + } else if (lastRecordedPass(previousPass) != NULL && !previousPassStorage.isNull()) { + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + if (UNEXPECTED(!pt_expression_result_storage_merge_results(originalStorage, previousPassStorage.raw()))) return zv::Val(); + zval *stmts = ptsh::readNodeProperty(pt_dwh_stmts_site, stmt, PT_LC("stmts")); + if (UNEXPECTED(stmts == NULL)) return zv::Val(); + zv::Val stmtsHold = zv::Val::copyOf(zv::Ref(stmts)); + zv::Val walked = pt_node_scope_resolver_process_loop_final_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage, nodeCallback, context, previousPass.raw()); + if (UNEXPECTED(walked.isUndef())) return zv::Val(); + bodyScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); + if (UNEXPECTED(bodyScopeResult.isUndef())) return zv::Val(); } else { zval *stmts = ptsh::readNodeProperty(pt_dwh_stmts_site, stmt, PT_LC("stmts")); if (UNEXPECTED(stmts == NULL)) return zv::Val(); diff --git a/turbo-ext/src/ForeachHandler.cpp b/turbo-ext/src/ForeachHandler.cpp index 8debfcbb8d2..75769cda403 100644 --- a/turbo-ext/src/ForeachHandler.cpp +++ b/turbo-ext/src/ForeachHandler.cpp @@ -278,6 +278,15 @@ zv::Val unionOfList(zv::Arr &list) } // namespace +/* the last convergence pass when it recorded its emissions (its + * [entries, recording] record), NULL otherwise */ +static zval *lastRecordedPass(zv::Val &previousPass) +{ + if (!previousPass.ref().isArray()) return NULL; + zval *recording = zend_hash_index_find(Z_ARRVAL_P(previousPass.raw()), 1); + return recording != NULL && Z_TYPE_P(recording) == IS_OBJECT ? previousPass.raw() : NULL; +} + namespace phpstanturbo { /* Mirrors PHPStan\Analyser\StmtHandler\ForeachHandler; UNDEF = pending @@ -437,6 +446,8 @@ class ForeachHandler } zv::Val replayBodyRecording, replayPassStorage, replayPassResult, replayEntryScope; + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); zv::Val unrolledEndScope; bool unrolled = false; zend_long unrolledTotalKeys = 0; @@ -489,8 +500,6 @@ class ForeachHandler zv::Val prevEntryScope; // each pass after the first re-walks only the statements whose // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - zv::Val previousPass = zv::Val::null(); - zv::Val previousPassStorage = zv::Val::null(); bool bodyIsReplayable; if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, parts.stmts.raw(), bodyIsReplayable))) return zv::Val(); do { @@ -512,7 +521,7 @@ class ForeachHandler if (UNEXPECTED(storage.isUndef())) return zv::Val(); // the statements this pass replays answer from the results the previous pass stored if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); - zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); + zv::Val bodyRecording = ptlh::newPassNodeCallback(true); if (UNEXPECTED(bodyRecording.isUndef())) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope.raw()), storage.raw()))) return zv::Val(); zv::Val bodyScopeResult; @@ -571,6 +580,7 @@ class ForeachHandler bodyScope = pt_mutating_scope_merge_with(Z_OBJ_P(bodyScope.raw()), nonEmptyIterateeScope.raw()); if (UNEXPECTED(bodyScope.isUndef())) return zv::Val(); zv::Val finalEntryScope = zv::Val::copyOf(bodyScope.ref()); + zval *lastPass = lastRecordedPass(previousPass); zval *storage = originalStorage; bodyScope = enterForeach(nodeScopeResolver, bodyScope.raw(), storage, originalScope.raw(), stmt, parts, foreachIterateeType.raw(), foreachNativeIterateeType.raw(), nodeCallback); if (UNEXPECTED(bodyScope.isUndef())) return zv::Val(); @@ -586,6 +596,17 @@ class ForeachHandler if (UNEXPECTED(!pt_expression_result_storage_merge_results(originalStorage, replayPassStorage.raw()))) return zv::Val(); if (UNEXPECTED(!pt_node_scope_resolver_replay_recording(nodeScopeResolver, replayBodyRecording.raw(), nodeCallback, originalStorage, scope.raw()))) return zv::Val(); finalScopeResult = std::move(replayPassResult); + } else if (lastPass != NULL && !previousPassStorage.isNull()) { + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + if (UNEXPECTED(!pt_expression_result_storage_merge_results(originalStorage, previousPassStorage.raw()))) return zv::Val(); + zval *stmts = ptsh::readNodeProperty(pt_feh_stmts_site, stmt, PT_LC("stmts")); + if (UNEXPECTED(stmts == NULL)) return zv::Val(); + zv::Val stmtsHold = zv::Val::copyOf(zv::Ref(stmts)); + zv::Val walked = pt_node_scope_resolver_process_loop_final_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), storage, nodeCallback, context, lastPass); + if (UNEXPECTED(walked.isUndef())) return zv::Val(); + finalScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); + if (UNEXPECTED(finalScopeResult.isUndef())) return zv::Val(); } else { zv::Val finalPassContext; if (unrolled) { diff --git a/turbo-ext/src/NodeScopeResolver.cpp b/turbo-ext/src/NodeScopeResolver.cpp index 2ff537ed988..4cadeb7f040 100644 --- a/turbo-ext/src/NodeScopeResolver.cpp +++ b/turbo-ext/src/NodeScopeResolver.cpp @@ -645,6 +645,29 @@ class NodeScopeResolver return result; } + /* Mirrors processLoopFinalStmtNodes(). */ + zv::Val processLoopFinalStmtNodes(zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass) + { + zv::Val result; + pt_engine_with_stack([&]() { + zv::Val current = pt_mutating_scope_get_current_expression_result_storage(Z_OBJ_P(scope)); + if (UNEXPECTED(current.isUndef())) return; + bool pushStorage = !(current.ref().isObject() && Z_OBJ_P(current.raw()) == Z_OBJ_P(storage)); + if (pushStorage && UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope), storage))) return; + zval thisZv; + ZVAL_OBJ(&thisZv, self); + zval *statementsHandler = requireStatementsHandler(); + if (EXPECTED(statementsHandler != NULL)) { + result = pt_statements_handler_process_loop_final_stmt_nodes(statementsHandler, &thisZv, parentNode, stmts, scope, storage, nodeCallback, context, lastPass); + } + if (pushStorage) { + pt_finally([&]() { (void) pt_mutating_scope_pop_expression_result_storage(Z_OBJ_P(scope)); }); + if (UNEXPECTED(EG(exception))) result = zv::Val(); + } + }); + return result; + } + /* Mirrors processLoopPassStmtNodes(); previous IS_NULL for the first * pass, record receives this pass's [entries, recording] */ zv::Val processLoopPassStmtNodes(zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) @@ -1052,6 +1075,29 @@ class NodeScopeResolver return zv::Val(std::move(empty)); } + /* Mirrors isContextSensitiveStatement(). */ + [[nodiscard]] bool isContextSensitiveStatement(zval *stmt, bool &out) + { + zv::Val cached = pt_engine_node_get_attribute(Z_OBJ_P(stmt), PT_LC("convergenceContextSensitiveStatement")); + if (UNEXPECTED(cached.isUndef())) return false; + if (!cached.isNull()) { + if (UNEXPECTED(!cached.ref().isBool())) { + zend_type_error("PHPStan\\Analyser\\NodeScopeResolver::isContextSensitiveStatement(): Return value must be of type bool, %s returned", zend_zval_value_name(cached.raw())); + return false; + } + out = cached.ref().isTrue(); + return true; + } + + bool sensitive; + if (UNEXPECTED(!hasContextSensitiveConstruct(Z_OBJ_P(stmt), sensitive))) return false; + zval value; + ZVAL_BOOL(&value, sensitive); + if (UNEXPECTED(!pt_engine_node_set_attribute(Z_OBJ_P(stmt), PT_LC("convergenceContextSensitiveStatement"), &value))) return false; + out = sensitive; + return true; + } + /* Mirrors isReplayableConvergenceBody(). */ [[nodiscard]] bool isReplayableConvergenceBody(zval *loopNode, HashTable *bodyStmts, bool &out) { @@ -2152,6 +2198,22 @@ zv::Val pt_node_scope_resolver_process_stmt_nodes_internal(zval *nodeScopeResolv return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).thisProcessStmtNodesInternal(parentNode, stmts, scope, storage, nodeCallback, context); } +zv::Val pt_node_scope_resolver_process_loop_final_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass) +{ + if (isNativeResolver(nodeScopeResolver)) return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).processLoopFinalStmtNodes(parentNode, stmts, scope, storage, nodeCallback, context, lastPass); + zv::Args argv{parentNode, stmts, scope, storage, nodeCallback, context, lastPass}; + return pt_type_call(Z_OBJ_P(nodeScopeResolver), PT_LC("processloopfinalstmtnodes"), 7, argv); +} + +bool pt_node_scope_resolver_is_context_sensitive_statement(zval *nodeScopeResolver, zval *stmt, bool &out) +{ + if (isNativeResolver(nodeScopeResolver)) return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).isContextSensitiveStatement(stmt, out); + zv::Val result = pt_type_call(Z_OBJ_P(nodeScopeResolver), PT_LC("iscontextsensitivestatement"), 1, stmt); + if (UNEXPECTED(result.isUndef())) return false; + out = zend_is_true(result.raw()); + return true; +} + zv::Val pt_node_scope_resolver_process_loop_pass_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) { if (isNativeResolver(nodeScopeResolver)) return NodeScopeResolver(Z_OBJ_P(nodeScopeResolver)).processLoopPassStmtNodes(parentNode, stmts, scope, storage, nodeCallback, context, previous, record); @@ -2409,6 +2471,7 @@ PT_MINIT_REGISTRATION(pt_register_node_scope_resolver) cls.classConstantLong("LOOP_SCOPE_ITERATIONS", 3); cls.classConstantLong("GENERALIZE_AFTER_ITERATION", 1); cls.privateClassConstantString("REPLAYABLE_BODY_ATTRIBUTE", "convergenceReplayableBody"); + cls.privateClassConstantString("CONTEXT_SENSITIVE_STATEMENT_ATTRIBUTE", "convergenceContextSensitiveStatement"); ptdecl::NodeScopeResolver::declareProperties(cls); /* the real parameter class names: the DI container autowires the @@ -2492,6 +2555,20 @@ PT_MINIT_REGISTRATION(pt_register_node_scope_resolver) PT_RETURN_VAL(PT_NSR_THIS.processStmtNodes(parentNode, stmts, scope, nodeCallback, context)); }); + cls.method(sigs::processLoopFinalStmtNodes, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *parentNode, *stmts, *scope, *storage, *nodeCallback, *context, *lastPass; + ZEND_PARSE_PARAMETERS_START(7, 7) + Z_PARAM_OBJECT(parentNode) + Z_PARAM_ARRAY(stmts) + Z_PARAM_OBJECT(scope) + Z_PARAM_OBJECT(storage) + Z_PARAM_ZVAL(nodeCallback) + Z_PARAM_OBJECT(context) + Z_PARAM_ARRAY(lastPass) + ZEND_PARSE_PARAMETERS_END(); + PT_RETURN_VAL(PT_NSR_THIS.processLoopFinalStmtNodes(parentNode, stmts, scope, storage, nodeCallback, context, lastPass)); + }); + cls.method(sigs::processLoopPassStmtNodes, [](INTERNAL_FUNCTION_PARAMETERS) { zval *parentNode, *stmts, *scope, *storage, *nodeCallback, *context, *previous, *recordRef; ZEND_PARSE_PARAMETERS_START(8, 8) @@ -2682,6 +2759,16 @@ PT_MINIT_REGISTRATION(pt_register_node_scope_resolver) PT_RETURN_VAL(PT_NSR_THIS.getImpurePointsFromPropertyHook(scope, propertyFetch, propertyReflection, hookName)); }); + cls.method(sigs::isContextSensitiveStatement, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *stmt; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(stmt) + ZEND_PARSE_PARAMETERS_END(); + bool out; + if (UNEXPECTED(!PT_NSR_THIS.isContextSensitiveStatement(stmt, out))) RETURN_THROWS(); + RETURN_BOOL(out); + }); + cls.method(sigs::isReplayableConvergenceBody, [](INTERNAL_FUNCTION_PARAMETERS) { zval *loopNode; HashTable *bodyStmts; diff --git a/turbo-ext/src/StatementsHandler.cpp b/turbo-ext/src/StatementsHandler.cpp index 6937cfa113c..e3134390598 100644 --- a/turbo-ext/src/StatementsHandler.cpp +++ b/turbo-ext/src/StatementsHandler.cpp @@ -663,6 +663,21 @@ class StatementsHandler zv::Val processLoopPassStmtNodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) { bool isRecording = Z_TYPE_P(nodeCallback) == IS_OBJECT && instanceof_function(Z_OBJCE_P(nodeCallback), pt_ce_recording_node_callback); + return walkAgainstPreviousPass(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, previous, isRecording ? nodeCallback : NULL, false, &record); + } + + /* Mirrors processLoopFinalStmtNodes(). UNDEF = pending exception */ + zv::Val processLoopFinalStmtNodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass) + { + return walkAgainstPreviousPass(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, lastPass, nodeCallback, true, NULL); + } + + /* Mirrors the private walkAgainstPreviousPass(): replayTarget NULL for + * none; record (a pass's [entries, recording]) is left alone by a final + * walk. UNDEF = pending exception */ + zv::Val walkAgainstPreviousPass(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zval *replayTarget, bool finalWalk, zv::Val *record) + { + bool isRecording = !finalWalk && Z_TYPE_P(nodeCallback) == IS_OBJECT && instanceof_function(Z_OBJCE_P(nodeCallback), pt_ce_recording_node_callback); zval *recording = isRecording ? nodeCallback : NULL; bool hasPrevious = previous != NULL && Z_TYPE_P(previous) == IS_ARRAY; zval *previousEntries = hasPrevious ? zend_hash_index_find(Z_ARRVAL_P(previous), 0) : NULL; @@ -670,7 +685,7 @@ class StatementsHandler if (previousRecording != NULL && Z_TYPE_P(previousRecording) == IS_NULL) previousRecording = NULL; // a replayed statement's emissions come from the previous recording; // labels make the statement order no straight line - bool canReplay = hasPrevious && previousEntries != NULL && (recording == NULL || previousRecording != NULL); + bool canReplay = hasPrevious && previousEntries != NULL && (replayTarget == NULL || previousRecording != NULL); zv::Val stmtsHeld = zv::Val::copyOf(zv::Ref(stmts)); if (canReplay) { bool hasLabels; @@ -679,21 +694,48 @@ class StatementsHandler } HashTable *stmtsTable = Z_ARRVAL_P(stmtsHeld.raw()); zend_long stmtCount = zend_hash_num_elements(stmtsTable); + // the last statement a final walk walks whatever the state + zend_long lastWalked = -1; + if (canReplay && finalWalk) { + for (zend_long i = stmtCount - 1; i >= 0; i--) { + zval *stmt = zend_hash_index_find(stmtsTable, (zend_ulong) i); + if (UNEXPECTED(stmt == NULL)) { + zend_throw_error(NULL, "phpstan_turbo: the loop body's statements are not a list"); + return zv::Val(); + } + ZVAL_DEREF(stmt); + bool sensitive; + if (UNEXPECTED(!pt_node_scope_resolver_is_context_sensitive_statement(nodeScopeResolver, stmt, sensitive))) return zv::Val(); + if (sensitive) { + lastWalked = i; + break; + } + } + } zv::Val state = pt_statement_list_walk_state_new(scope); if (UNEXPECTED(state.isUndef())) return zv::Val(); - zv::Arr entries = zv::Arr::create((uint32_t) stmtCount + 1); + zv::Arr entries = zv::Arr::create(finalWalk ? 0 : (uint32_t) stmtCount + 1); for (zend_long i = 0; i < stmtCount; i++) { - zv::Val entry = snapshotPassEntry(state.raw(), recording); - if (UNEXPECTED(entry.isUndef())) return zv::Val(); - zend_long entryCount = zval_get_long(entryOffset(entry.raw())); - entries.push(std::move(entry)); + zend_long entryCount = 0; + if (!finalWalk) { + zv::Val entry = snapshotPassEntry(state.raw(), recording); + if (UNEXPECTED(entry.isUndef())) return zv::Val(); + entryCount = zval_get_long(entryOffset(entry.raw())); + entries.push(std::move(entry)); + } zval *stmt = zend_hash_index_find(stmtsTable, (zend_ulong) i); if (UNEXPECTED(stmt == NULL)) { zend_throw_error(NULL, "phpstan_turbo: the loop body's statements are not a list"); return zv::Val(); } ZVAL_DEREF(stmt); - if (canReplay) { + bool mayReplay = canReplay; + if (mayReplay && i <= lastWalked) { + bool sensitive; + if (UNEXPECTED(!pt_node_scope_resolver_is_context_sensitive_statement(nodeScopeResolver, stmt, sensitive))) return zv::Val(); + mayReplay = !sensitive; + } + if (mayReplay) { zval *recordedEntryPair = entryAt(previousEntries, i); zval *recordedExitPair = recordedEntryPair != NULL ? entryAt(previousEntries, i + 1) : NULL; if (UNEXPECTED(recordedExitPair == NULL)) return zv::Val(); @@ -710,15 +752,16 @@ class StatementsHandler differingRoots = pt_mutating_scope_get_differing_variable_roots(Z_OBJ_P(OBJ_PROP_NUM(stateObject, stateSlots::scope)), Z_OBJ_P(OBJ_PROP_NUM(recordedEntryObject, stateSlots::scope))); if (UNEXPECTED(differingRoots.isUndef())) return zv::Val(); } - if (differingRoots.ref().isArray() && zend_hash_num_elements(Z_ARRVAL_P(differingRoots.raw())) == 0) { + if (i > lastWalked && differingRoots.ref().isArray() && zend_hash_num_elements(Z_ARRVAL_P(differingRoots.raw())) == 0) { // converged with the previous pass: the rest of it stands zval *recordedEndPair = entryAt(previousEntries, stmtCount); if (UNEXPECTED(recordedEndPair == NULL)) return zv::Val(); zval *recordedEnd = entryState(recordedEndPair); zend_long endOffset = zval_get_long(entryOffset(recordedEndPair)); - if (recording != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, endOffset, recording, storage, scope))) return zv::Val(); + if (replayTarget != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, endOffset, replayTarget, storage, scope))) return zv::Val(); if (UNEXPECTED(!appendRecordedStatementResults(state.raw(), recordedEntry, recordedEnd))) return zv::Val(); zv::Ref(OBJ_PROP_NUM(Z_OBJ_P(state.raw()), stateSlots::scope)).assign(zv::Val::copyOf(zv::Ref(OBJ_PROP_NUM(Z_OBJ_P(recordedEnd), stateSlots::scope)))); + if (finalWalk) return pt_statement_list_walk_state_to_result(state.raw()); // $entries[$j] = [$previous[0][$j][0], $base + $previous[0][$j][1] - $offset] for $j >= $i zv::Arr adopted = zv::Arr::create((uint32_t) stmtCount + 1); zend_long j = 0; @@ -734,7 +777,7 @@ class StatementsHandler shifted.push(zv::Val::integer(entryCount + zval_get_long(entryOffset(pair)) - offset)); adopted.push(zv::Val(std::move(shifted))); } - record = passRecord(zv::Val(std::move(adopted)), recording); + *record = passRecord(zv::Val(std::move(adopted)), recording); return pt_statement_list_walk_state_to_result(state.raw()); } bool reWalk = differingRoots.isNull(); @@ -744,7 +787,7 @@ class StatementsHandler if (UNEXPECTED(!statementMentionsAnyVariable(stmt, mentioned.raw(), reWalk))) return zv::Val(); } if (!reWalk) { - if (recording != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, nextOffset, recording, storage, scope))) return zv::Val(); + if (replayTarget != NULL && previousRecording != NULL && UNEXPECTED(!pt_node_scope_resolver_replay_recording_range(nodeScopeResolver, previousRecording, offset, nextOffset, replayTarget, storage, scope))) return zv::Val(); zend_long exitPointCount = zend_hash_num_elements(Z_ARRVAL_P(OBJ_PROP_NUM(stateObject, stateSlots::exitPoints))); zend_long throwPointCount = zend_hash_num_elements(Z_ARRVAL_P(OBJ_PROP_NUM(stateObject, stateSlots::throwPoints))); if (UNEXPECTED(!appendRecordedStatementResults(state.raw(), recordedEntry, recordedExit))) return zv::Val(); @@ -759,10 +802,12 @@ class StatementsHandler } if (UNEXPECTED(!processStatementStep(nodeScopeResolver, parentNode, stmts, i, stmt, state.raw(), storage, nodeCallback, context, false))) return zv::Val(); } - zv::Val last = snapshotPassEntry(state.raw(), recording); - if (UNEXPECTED(last.isUndef())) return zv::Val(); - entries.push(std::move(last)); - record = passRecord(zv::Val(std::move(entries)), recording); + if (!finalWalk) { + zv::Val last = snapshotPassEntry(state.raw(), recording); + if (UNEXPECTED(last.isUndef())) return zv::Val(); + entries.push(std::move(last)); + *record = passRecord(zv::Val(std::move(entries)), recording); + } return pt_statement_list_walk_state_to_result(state.raw()); } @@ -3447,6 +3492,13 @@ zv::Val pt_statements_handler_do_process_stmt_nodes(zval *handler, zval *nodeSco return pt_type_call(Z_OBJ_P(handler), PT_LC("doprocessstmtnodes"), 7, argv); } +zv::Val pt_statements_handler_process_loop_final_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass) +{ + if (isNativeHandler(handler)) return StatementsHandler(Z_OBJ_P(handler)).processLoopFinalStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, lastPass); + zv::Args argv{nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, lastPass}; + return pt_type_call(Z_OBJ_P(handler), PT_LC("processloopfinalstmtnodes"), 8, argv); +} + zv::Val pt_statements_handler_process_loop_pass_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record) { if (isNativeHandler(handler)) return StatementsHandler(Z_OBJ_P(handler)).processLoopPassStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, previous, record); @@ -3569,6 +3621,21 @@ PT_MINIT_REGISTRATION(pt_register_statements_handler) PT_RETURN_VAL(std::move(result)); }); + cls.method(sigs::processLoopFinalStmtNodes, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *nodeScopeResolver, *parentNode, *stmts, *scope, *storage, *nodeCallback, *context, *lastPass; + ZEND_PARSE_PARAMETERS_START(8, 8) + Z_PARAM_OBJECT(nodeScopeResolver) + Z_PARAM_OBJECT(parentNode) + Z_PARAM_ARRAY(stmts) + Z_PARAM_OBJECT(scope) + Z_PARAM_OBJECT(storage) + Z_PARAM_ZVAL(nodeCallback) + Z_PARAM_OBJECT(context) + Z_PARAM_ARRAY(lastPass) + ZEND_PARSE_PARAMETERS_END(); + PT_RETURN_VAL(StatementsHandler(Z_OBJ_P(ZEND_THIS)).processLoopFinalStmtNodes(nodeScopeResolver, parentNode, stmts, scope, storage, nodeCallback, context, lastPass)); + }); + cls.method(sigs::getVariableMentionFlow, [](INTERNAL_FUNCTION_PARAMETERS) { zval *stmt; ZEND_PARSE_PARAMETERS_START(1, 1) diff --git a/turbo-ext/src/WhileHandler.cpp b/turbo-ext/src/WhileHandler.cpp index 1cb1dfa442d..d87791b76ca 100644 --- a/turbo-ext/src/WhileHandler.cpp +++ b/turbo-ext/src/WhileHandler.cpp @@ -36,6 +36,15 @@ pt_property_site pt_wh_stmts_site; } // namespace +/* the last convergence pass when it recorded its emissions (its + * [entries, recording] record), NULL otherwise */ +static zval *lastRecordedPass(zv::Val &previousPass) +{ + if (!previousPass.ref().isArray()) return NULL; + zval *recording = zend_hash_index_find(Z_ARRVAL_P(previousPass.raw()), 1); + return recording != NULL && Z_TYPE_P(recording) == IS_OBJECT ? previousPass.raw() : NULL; +} + namespace phpstanturbo { /* Mirrors PHPStan\Analyser\StmtHandler\WhileHandler; UNDEF = pending @@ -123,6 +132,8 @@ class WhileHandler zv::Val replayCondRecording, replayBodyRecording, replayCondResult, replayPassStorage, replayPassResult; zv::Val prevEntryScope; + zv::Val previousPass = zv::Val::null(); + zv::Val previousPassStorage = zv::Val::null(); if (isTopLevel) { zend_long count = 0; zval *stmts = ptsh::readNodeProperty(pt_wh_stmts_site, stmt, PT_LC("stmts")); @@ -131,8 +142,6 @@ class WhileHandler if (UNEXPECTED(!pt_node_scope_resolver_is_replayable_convergence_body(nodeScopeResolver, stmt, stmts, bodyIsReplayable))) return zv::Val(); // each pass after the first re-walks only the statements whose // entry state differs from the previous pass's (StatementsHandler::processLoopPassStmtNodes()) - zv::Val previousPass = zv::Val::null(); - zv::Val previousPassStorage = zv::Val::null(); do { zv::Val prevScope = zv::Val::copyOf(bodyScope.ref()); bodyScope = pt_mutating_scope_merge_with(Z_OBJ_P(bodyScope.raw()), scope.raw()); @@ -154,7 +163,7 @@ class WhileHandler if (!previousPassStorage.isNull() && UNEXPECTED(!pt_expression_result_storage_merge_results(storage.raw(), previousPassStorage.raw()))) return zv::Val(); zv::Val condRecording = ptlh::newPassNodeCallback(bodyIsReplayable); if (UNEXPECTED(condRecording.isUndef())) return zv::Val(); - zv::Val bodyRecording = ptlh::newPassNodeCallback(bodyIsReplayable); + zv::Val bodyRecording = ptlh::newPassNodeCallback(true); if (UNEXPECTED(bodyRecording.isUndef())) return zv::Val(); if (UNEXPECTED(!pt_mutating_scope_push_expression_result_storage(Z_OBJ_P(scope.raw()), storage.raw()))) return zv::Val(); zv::Val passCondResult, bodyScopeResult, backEdgeScope; @@ -243,6 +252,9 @@ class WhileHandler bodyCondResult = std::move(replayCondResult); finalScopeResult = std::move(replayPassResult); } else { + zval *lastPass = !previousPassStorage.isNull() ? lastRecordedPass(previousPass) : NULL; + // before the condition's real walk stores its own results + if (lastPass != NULL && UNEXPECTED(!pt_expression_result_storage_merge_results(originalStorage, previousPassStorage.raw()))) return zv::Val(); zval *cond = ptsh::readNodeProperty(pt_wh_cond_site, stmt, PT_LC("cond")); if (UNEXPECTED(cond == NULL)) return zv::Val(); zv::Val condHold = zv::Val::copyOf(zv::Ref(cond)); @@ -260,7 +272,11 @@ class WhileHandler zval *stmts = ptsh::readNodeProperty(pt_wh_stmts_site, stmt, PT_LC("stmts")); if (UNEXPECTED(stmts == NULL)) return zv::Val(); zv::Val stmtsHold = zv::Val::copyOf(zv::Ref(stmts)); - zv::Val walked = pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), finalStorage, nodeCallback, context); + // the last pass stands in for the statements whose state it walked + // with - they replay through the real callback + zv::Val walked = lastPass != NULL + ? pt_node_scope_resolver_process_loop_final_stmt_nodes(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), finalStorage, nodeCallback, context, lastPass) + : pt_node_scope_resolver_process_stmt_nodes_internal(nodeScopeResolver, stmt, stmtsHold.raw(), bodyScope.raw(), finalStorage, nodeCallback, context); if (UNEXPECTED(walked.isUndef())) return zv::Val(); finalScopeResult = pt_internal_statement_result_filter_out_loop_exit_points(walked.raw()); if (UNEXPECTED(finalScopeResult.isUndef())) return zv::Val(); diff --git a/turbo-ext/src/generated/NodeScopeResolver.h b/turbo-ext/src/generated/NodeScopeResolver.h index aa8736e97ad..754c18e3fcb 100644 --- a/turbo-ext/src/generated/NodeScopeResolver.h +++ b/turbo-ext/src/generated/NodeScopeResolver.h @@ -92,75 +92,78 @@ inline constexpr char strings[] = "PHPStan\\Analyser\\StatementContext\0" /* 864 */ "PHPStan\\Analyser\\StatementResult\0" /* 898 */ "processStmtNodes\0" /* 931 */ - "previous\0" /* 948 */ - "record\0" /* 957 */ - "PHPStan\\Analyser\\InternalStatementResult\0" /* 964 */ - "processLoopPassStmtNodes\0" /* 1005 */ - "processStmtNodesInternal\0" /* 1030 */ - "stmt\0" /* 1055 */ - "PhpParser\\Node\\Stmt\0" /* 1060 */ - "processStmtNode\0" /* 1080 */ - "fileName\0" /* 1096 */ - "isAnalysedFile\0" /* 1105 */ - "isReturningStoredExpressionResults\0" /* 1120 */ - "isConsumingStoredExpressionResults\0" /* 1155 */ - "lookForSetAllowedUndefinedExpressions\0" /* 1190 */ - "lookForUnsetAllowedUndefinedExpressions\0" /* 1228 */ - "callback\0" /* 1268 */ - "Closure\0" /* 1277 */ - "lookForExpressionCallback\0" /* 1285 */ - "PHPStan\\Analyser\\ExpressionContext\0" /* 1311 */ - "processExprNodeConsumingStored\0" /* 1346 */ - "processExprOnDemand\0" /* 1377 */ - "readStoredResult\0" /* 1397 */ - "PHPStan\\Type\\Type\0" /* 1414 */ - "readTypeOfMaybeStored\0" /* 1432 */ - "findScopeStateType\0" /* 1454 */ - "readScopeStateOrSyntheticType\0" /* 1473 */ - "requireScopeStateType\0" /* 1503 */ - "caller\0" /* 1525 */ - "guardAgainstUnprocessedRealNode\0" /* 1532 */ - "processSyntheticOnDemand\0" /* 1564 */ - "processExprNode\0" /* 1589 */ - "processExprNodeInternal\0" /* 1605 */ - "propertyFetch\0" /* 1629 */ - "PhpParser\\Node\\Expr\\PropertyFetch\0" /* 1643 */ - "propertyReflection\0" /* 1677 */ - "PHPStan\\Reflection\\Php\\PhpPropertyReflection\0" /* 1696 */ - "hookName\0" /* 1741 */ - "getImpurePointsFromPropertyHook\0" /* 1750 */ - "isPropertyHookBackingValueAccess\0" /* 1782 */ - "getAssignedVariables\0" /* 1815 */ - "loopNode\0" /* 1836 */ - "bodyStmts\0" /* 1845 */ - "isReplayableConvergenceBody\0" /* 1855 */ - "node\0" /* 1883 */ - "hasContextSensitiveConstruct\0" /* 1888 */ - "gatherer\0" /* 1917 */ - "pushNodeGatherer\0" /* 1926 */ - "popNodeGatherer\0" /* 1943 */ - "suspendNodeGatherers\0" /* 1959 */ - "gatherers\0" /* 1980 */ - "restoreNodeGatherers\0" /* 1990 */ - "recording\0" /* 2011 */ - "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 2021 */ - "replayRecording\0" /* 2060 */ - "from\0" /* 2076 */ - "to\0" /* 2081 */ - "replayRecordingRange\0" /* 2084 */ - "callNodeCallbackWithExpression\0" /* 2105 */ - "callNodeCallback\0" /* 2136 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 2153 */ - "observingTemplateArgumentFrame\0" /* 2201 */ - "returnedResult\0" /* 2232 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 2247 */ - "collectReturnSend\0" /* 2301 */ - "keyType\0" /* 2319 */ - "valueType\0" /* 2327 */ - "collectYieldSend\0" /* 2337 */ - "delegatedType\0" /* 2354 */ - "collectYieldFromSend\0" /* 2368 */ - "getDeclaredReturnType"; /* 2389 */ + "lastPass\0" /* 948 */ + "PHPStan\\Analyser\\InternalStatementResult\0" /* 957 */ + "processLoopFinalStmtNodes\0" /* 998 */ + "previous\0" /* 1024 */ + "record\0" /* 1033 */ + "processLoopPassStmtNodes\0" /* 1040 */ + "processStmtNodesInternal\0" /* 1065 */ + "stmt\0" /* 1090 */ + "PhpParser\\Node\\Stmt\0" /* 1095 */ + "processStmtNode\0" /* 1115 */ + "fileName\0" /* 1131 */ + "isAnalysedFile\0" /* 1140 */ + "isReturningStoredExpressionResults\0" /* 1155 */ + "isConsumingStoredExpressionResults\0" /* 1190 */ + "lookForSetAllowedUndefinedExpressions\0" /* 1225 */ + "lookForUnsetAllowedUndefinedExpressions\0" /* 1263 */ + "callback\0" /* 1303 */ + "Closure\0" /* 1312 */ + "lookForExpressionCallback\0" /* 1320 */ + "PHPStan\\Analyser\\ExpressionContext\0" /* 1346 */ + "processExprNodeConsumingStored\0" /* 1381 */ + "processExprOnDemand\0" /* 1412 */ + "readStoredResult\0" /* 1432 */ + "PHPStan\\Type\\Type\0" /* 1449 */ + "readTypeOfMaybeStored\0" /* 1467 */ + "findScopeStateType\0" /* 1489 */ + "readScopeStateOrSyntheticType\0" /* 1508 */ + "requireScopeStateType\0" /* 1538 */ + "caller\0" /* 1560 */ + "guardAgainstUnprocessedRealNode\0" /* 1567 */ + "processSyntheticOnDemand\0" /* 1599 */ + "processExprNode\0" /* 1624 */ + "processExprNodeInternal\0" /* 1640 */ + "propertyFetch\0" /* 1664 */ + "PhpParser\\Node\\Expr\\PropertyFetch\0" /* 1678 */ + "propertyReflection\0" /* 1712 */ + "PHPStan\\Reflection\\Php\\PhpPropertyReflection\0" /* 1731 */ + "hookName\0" /* 1776 */ + "getImpurePointsFromPropertyHook\0" /* 1785 */ + "isPropertyHookBackingValueAccess\0" /* 1817 */ + "getAssignedVariables\0" /* 1850 */ + "loopNode\0" /* 1871 */ + "bodyStmts\0" /* 1880 */ + "isReplayableConvergenceBody\0" /* 1890 */ + "isContextSensitiveStatement\0" /* 1918 */ + "node\0" /* 1946 */ + "hasContextSensitiveConstruct\0" /* 1951 */ + "gatherer\0" /* 1980 */ + "pushNodeGatherer\0" /* 1989 */ + "popNodeGatherer\0" /* 2006 */ + "suspendNodeGatherers\0" /* 2022 */ + "gatherers\0" /* 2043 */ + "restoreNodeGatherers\0" /* 2053 */ + "recording\0" /* 2074 */ + "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 2084 */ + "replayRecording\0" /* 2123 */ + "from\0" /* 2139 */ + "to\0" /* 2144 */ + "replayRecordingRange\0" /* 2147 */ + "callNodeCallbackWithExpression\0" /* 2168 */ + "callNodeCallback\0" /* 2199 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 2216 */ + "observingTemplateArgumentFrame\0" /* 2264 */ + "returnedResult\0" /* 2295 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 2310 */ + "collectReturnSend\0" /* 2364 */ + "keyType\0" /* 2382 */ + "valueType\0" /* 2390 */ + "collectYieldSend\0" /* 2400 */ + "delegatedType\0" /* 2417 */ + "collectYieldFromSend\0" /* 2431 */ + "getDeclaredReturnType"; /* 2452 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 10), /* __construct $container */ reg::packed(48, 0, 73), /* __construct $templateArgumentObserver */ @@ -194,29 +197,37 @@ inline constexpr reg::PackedArg args[] = { reg::packed(539, MAY_BE_CALLABLE), /* processStmtNodes $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNodes $context */ reg::packed(371, 0, 898), /* processStmtNodes return */ + reg::packed(832, 0, 843), /* processLoopFinalStmtNodes $parentNode */ + reg::packed(858, MAY_BE_ARRAY), /* processLoopFinalStmtNodes $stmts */ + reg::packed(502, 0, 508), /* processLoopFinalStmtNodes $scope */ + reg::packed(614, 0, 622), /* processLoopFinalStmtNodes $storage */ + reg::packed(539, MAY_BE_CALLABLE), /* processLoopFinalStmtNodes $nodeCallback */ + reg::packed(761, 0, 864), /* processLoopFinalStmtNodes $context */ + reg::packed(948, MAY_BE_ARRAY), /* processLoopFinalStmtNodes $lastPass */ + reg::packed(371, 0, 957), /* processLoopFinalStmtNodes return */ reg::packed(832, 0, 843), /* processLoopPassStmtNodes $parentNode */ reg::packed(858, MAY_BE_ARRAY), /* processLoopPassStmtNodes $stmts */ reg::packed(502, 0, 508), /* processLoopPassStmtNodes $scope */ reg::packed(614, 0, 622), /* processLoopPassStmtNodes $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processLoopPassStmtNodes $nodeCallback */ reg::packed(761, 0, 864), /* processLoopPassStmtNodes $context */ - reg::packed(948, MAY_BE_NULL | MAY_BE_ARRAY), /* processLoopPassStmtNodes $previous */ - reg::packed(957, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, true, false), /* processLoopPassStmtNodes $record */ - reg::packed(371, 0, 964), /* processLoopPassStmtNodes return */ + reg::packed(1024, MAY_BE_NULL | MAY_BE_ARRAY), /* processLoopPassStmtNodes $previous */ + reg::packed(1033, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, true, false), /* processLoopPassStmtNodes $record */ + reg::packed(371, 0, 957), /* processLoopPassStmtNodes return */ reg::packed(832, 0, 843), /* processStmtNodesInternal $parentNode */ reg::packed(858, MAY_BE_ARRAY), /* processStmtNodesInternal $stmts */ reg::packed(502, 0, 508), /* processStmtNodesInternal $scope */ reg::packed(614, 0, 622), /* processStmtNodesInternal $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processStmtNodesInternal $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNodesInternal $context */ - reg::packed(371, 0, 964), /* processStmtNodesInternal return */ - reg::packed(1055, 0, 1060), /* processStmtNode $stmt */ + reg::packed(371, 0, 957), /* processStmtNodesInternal return */ + reg::packed(1090, 0, 1095), /* processStmtNode $stmt */ reg::packed(502, 0, 508), /* processStmtNode $scope */ reg::packed(614, 0, 622), /* processStmtNode $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processStmtNode $nodeCallback */ reg::packed(761, 0, 864), /* processStmtNode $context */ - reg::packed(371, 0, 964), /* processStmtNode return */ - reg::packed(1096, MAY_BE_STRING), /* isAnalysedFile $fileName */ + reg::packed(371, 0, 957), /* processStmtNode return */ + reg::packed(1131, MAY_BE_STRING), /* isAnalysedFile $fileName */ reg::packed(371, MAY_BE_BOOL), /* isAnalysedFile return */ reg::packed(371, MAY_BE_BOOL), /* isReturningStoredExpressionResults return */ reg::packed(371, MAY_BE_BOOL), /* isConsumingStoredExpressionResults return */ @@ -228,14 +239,14 @@ inline constexpr reg::PackedArg args[] = { reg::packed(371, 0, 508), /* lookForUnsetAllowedUndefinedExpressions return */ reg::packed(502, 0, 508), /* lookForExpressionCallback $scope */ reg::packed(663, 0, 668), /* lookForExpressionCallback $expr */ - reg::packed(1268, 0, 1277), /* lookForExpressionCallback $callback */ + reg::packed(1303, 0, 1312), /* lookForExpressionCallback $callback */ reg::packed(371, 0, 508), /* lookForExpressionCallback return */ - reg::packed(1055, 0, 1060), /* processExprNodeConsumingStored $stmt */ + reg::packed(1090, 0, 1095), /* processExprNodeConsumingStored $stmt */ reg::packed(663, 0, 668), /* processExprNodeConsumingStored $expr */ reg::packed(502, 0, 508), /* processExprNodeConsumingStored $scope */ reg::packed(614, 0, 622), /* processExprNodeConsumingStored $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNodeConsumingStored $nodeCallback */ - reg::packed(761, 0, 1311), /* processExprNodeConsumingStored $context */ + reg::packed(761, 0, 1346), /* processExprNodeConsumingStored $context */ reg::packed(371, 0, 705), /* processExprNodeConsumingStored return */ reg::packed(663, 0, 668), /* processExprOnDemand $expr */ reg::packed(502, 0, 508), /* processExprOnDemand $scope */ @@ -246,65 +257,67 @@ inline constexpr reg::PackedArg args[] = { reg::packed(371, 0, 705), /* readStoredResult return */ reg::packed(663, 0, 668), /* readTypeOfMaybeStored $expr */ reg::packed(502, 0, 508), /* readTypeOfMaybeStored $scope */ - reg::packed(371, 0, 1414), /* readTypeOfMaybeStored return */ + reg::packed(371, 0, 1449), /* readTypeOfMaybeStored return */ reg::packed(663, 0, 668), /* findScopeStateType $expr */ reg::packed(502, 0, 508), /* findScopeStateType $scope */ - reg::packed(371, MAY_BE_NULL, 1414), /* findScopeStateType return */ + reg::packed(371, MAY_BE_NULL, 1449), /* findScopeStateType return */ reg::packed(663, 0, 668), /* readScopeStateOrSyntheticType $expr */ reg::packed(502, 0, 508), /* readScopeStateOrSyntheticType $scope */ - reg::packed(371, 0, 1414), /* readScopeStateOrSyntheticType return */ + reg::packed(371, 0, 1449), /* readScopeStateOrSyntheticType return */ reg::packed(663, 0, 668), /* requireScopeStateType $expr */ reg::packed(502, 0, 508), /* requireScopeStateType $scope */ - reg::packed(371, 0, 1414), /* requireScopeStateType return */ + reg::packed(371, 0, 1449), /* requireScopeStateType return */ reg::packed(663, 0, 668), /* guardAgainstUnprocessedRealNode $expr */ - reg::packed(1525, MAY_BE_STRING), /* guardAgainstUnprocessedRealNode $caller */ + reg::packed(1560, MAY_BE_STRING), /* guardAgainstUnprocessedRealNode $caller */ reg::packed(371, MAY_BE_VOID), /* guardAgainstUnprocessedRealNode return */ reg::packed(663, 0, 668), /* processSyntheticOnDemand $expr */ reg::packed(502, 0, 508), /* processSyntheticOnDemand $scope */ reg::packed(371, 0, 705), /* processSyntheticOnDemand return */ - reg::packed(1055, 0, 1060), /* processExprNode $stmt */ + reg::packed(1090, 0, 1095), /* processExprNode $stmt */ reg::packed(663, 0, 668), /* processExprNode $expr */ reg::packed(502, 0, 508), /* processExprNode $scope */ reg::packed(614, 0, 622), /* processExprNode $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNode $nodeCallback */ - reg::packed(761, 0, 1311), /* processExprNode $context */ + reg::packed(761, 0, 1346), /* processExprNode $context */ reg::packed(371, 0, 705), /* processExprNode return */ - reg::packed(1055, 0, 1060), /* processExprNodeInternal $stmt */ + reg::packed(1090, 0, 1095), /* processExprNodeInternal $stmt */ reg::packed(663, 0, 668), /* processExprNodeInternal $expr */ reg::packed(502, 0, 508), /* processExprNodeInternal $scope */ reg::packed(614, 0, 622), /* processExprNodeInternal $storage */ reg::packed(539, MAY_BE_CALLABLE), /* processExprNodeInternal $nodeCallback */ - reg::packed(761, 0, 1311), /* processExprNodeInternal $context */ + reg::packed(761, 0, 1346), /* processExprNodeInternal $context */ reg::packed(371, 0, 705), /* processExprNodeInternal return */ reg::packed(502, 0, 508), /* getImpurePointsFromPropertyHook $scope */ - reg::packed(1629, 0, 1643), /* getImpurePointsFromPropertyHook $propertyFetch */ - reg::packed(1677, 0, 1696), /* getImpurePointsFromPropertyHook $propertyReflection */ - reg::packed(1741, MAY_BE_STRING), /* getImpurePointsFromPropertyHook $hookName */ + reg::packed(1664, 0, 1678), /* getImpurePointsFromPropertyHook $propertyFetch */ + reg::packed(1712, 0, 1731), /* getImpurePointsFromPropertyHook $propertyReflection */ + reg::packed(1776, MAY_BE_STRING), /* getImpurePointsFromPropertyHook $hookName */ reg::packed(371, MAY_BE_ARRAY), /* getImpurePointsFromPropertyHook return */ reg::packed(502, 0, 508), /* isPropertyHookBackingValueAccess $scope */ - reg::packed(1629, 0, 1643), /* isPropertyHookBackingValueAccess $propertyFetch */ + reg::packed(1664, 0, 1678), /* isPropertyHookBackingValueAccess $propertyFetch */ reg::packed(371, MAY_BE_BOOL), /* isPropertyHookBackingValueAccess return */ reg::packed(663, 0, 668), /* getAssignedVariables $expr */ reg::packed(371, MAY_BE_ARRAY), /* getAssignedVariables return */ - reg::packed(1836, 0, 843), /* isReplayableConvergenceBody $loopNode */ - reg::packed(1845, MAY_BE_ARRAY), /* isReplayableConvergenceBody $bodyStmts */ + reg::packed(1871, 0, 843), /* isReplayableConvergenceBody $loopNode */ + reg::packed(1880, MAY_BE_ARRAY), /* isReplayableConvergenceBody $bodyStmts */ reg::packed(371, MAY_BE_BOOL), /* isReplayableConvergenceBody return */ - reg::packed(1883, 0, 843), /* hasContextSensitiveConstruct $node */ + reg::packed(1090, 0, 1095), /* isContextSensitiveStatement $stmt */ + reg::packed(371, MAY_BE_BOOL), /* isContextSensitiveStatement return */ + reg::packed(1946, 0, 843), /* hasContextSensitiveConstruct $node */ reg::packed(371, MAY_BE_BOOL), /* hasContextSensitiveConstruct return */ - reg::packed(1917, MAY_BE_CALLABLE), /* pushNodeGatherer $gatherer */ + reg::packed(1980, MAY_BE_CALLABLE), /* pushNodeGatherer $gatherer */ reg::packed(371, MAY_BE_VOID), /* pushNodeGatherer return */ reg::packed(371, MAY_BE_VOID), /* popNodeGatherer return */ reg::packed(371, MAY_BE_ARRAY), /* suspendNodeGatherers return */ - reg::packed(1980, MAY_BE_ARRAY), /* restoreNodeGatherers $gatherers */ + reg::packed(2043, MAY_BE_ARRAY), /* restoreNodeGatherers $gatherers */ reg::packed(371, MAY_BE_VOID), /* restoreNodeGatherers return */ - reg::packed(2011, 0, 2021), /* replayRecording $recording */ + reg::packed(2074, 0, 2084), /* replayRecording $recording */ reg::packed(539, MAY_BE_CALLABLE), /* replayRecording $nodeCallback */ reg::packed(614, 0, 622), /* replayRecording $storage */ reg::packed(502, 0, 508), /* replayRecording $scope */ reg::packed(371, MAY_BE_VOID), /* replayRecording return */ - reg::packed(2011, 0, 2021), /* replayRecordingRange $recording */ - reg::packed(2076, MAY_BE_LONG), /* replayRecordingRange $from */ - reg::packed(2081, MAY_BE_LONG), /* replayRecordingRange $to */ + reg::packed(2074, 0, 2084), /* replayRecordingRange $recording */ + reg::packed(2139, MAY_BE_LONG), /* replayRecordingRange $from */ + reg::packed(2144, MAY_BE_LONG), /* replayRecordingRange $to */ reg::packed(539, MAY_BE_CALLABLE), /* replayRecordingRange $nodeCallback */ reg::packed(614, 0, 622), /* replayRecordingRange $storage */ reg::packed(502, 0, 508), /* replayRecordingRange $scope */ @@ -313,27 +326,27 @@ inline constexpr reg::PackedArg args[] = { reg::packed(663, 0, 843), /* callNodeCallbackWithExpression $expr */ reg::packed(502, 0, 508), /* callNodeCallbackWithExpression $scope */ reg::packed(614, 0, 622), /* callNodeCallbackWithExpression $storage */ - reg::packed(761, 0, 1311), /* callNodeCallbackWithExpression $context */ + reg::packed(761, 0, 1346), /* callNodeCallbackWithExpression $context */ reg::packed(371, MAY_BE_VOID), /* callNodeCallbackWithExpression return */ reg::packed(539, MAY_BE_CALLABLE), /* callNodeCallback $nodeCallback */ - reg::packed(1883, 0, 843), /* callNodeCallback $node */ + reg::packed(1946, 0, 843), /* callNodeCallback $node */ reg::packed(502, 0, 508), /* callNodeCallback $scope */ reg::packed(614, 0, 622), /* callNodeCallback $storage */ reg::packed(371, MAY_BE_VOID), /* callNodeCallback return */ reg::packed(502, 0, 508), /* observingTemplateArgumentFrame $scope */ - reg::packed(371, MAY_BE_NULL, 2153), /* observingTemplateArgumentFrame return */ + reg::packed(371, MAY_BE_NULL, 2216), /* observingTemplateArgumentFrame return */ reg::packed(502, 0, 508), /* collectReturnSend $scope */ - reg::packed(2232, 0, 705), /* collectReturnSend $returnedResult */ - reg::packed(371, 0, 2247), /* collectReturnSend return */ + reg::packed(2295, 0, 705), /* collectReturnSend $returnedResult */ + reg::packed(371, 0, 2310), /* collectReturnSend return */ reg::packed(502, 0, 508), /* collectYieldSend $scope */ - reg::packed(2319, MAY_BE_NULL, 1414), /* collectYieldSend $keyType */ - reg::packed(2327, MAY_BE_NULL, 1414), /* collectYieldSend $valueType */ - reg::packed(371, 0, 2247), /* collectYieldSend return */ + reg::packed(2382, MAY_BE_NULL, 1449), /* collectYieldSend $keyType */ + reg::packed(2390, MAY_BE_NULL, 1449), /* collectYieldSend $valueType */ + reg::packed(371, 0, 2310), /* collectYieldSend return */ reg::packed(502, 0, 508), /* collectYieldFromSend $scope */ - reg::packed(2354, 0, 1414), /* collectYieldFromSend $delegatedType */ - reg::packed(371, 0, 2247), /* collectYieldFromSend return */ + reg::packed(2417, 0, 1449), /* collectYieldFromSend $delegatedType */ + reg::packed(371, 0, 2310), /* collectYieldFromSend return */ reg::packed(502, 0, 508), /* getDeclaredReturnType $scope */ - reg::packed(371, MAY_BE_NULL, 1414), /* getDeclaredReturnType return */ + reg::packed(371, MAY_BE_NULL, 1449), /* getDeclaredReturnType return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -349,44 +362,46 @@ inline constexpr sigtab::Sig processFileNodes = { { 597 /* processFileNodes */, inline constexpr sigtab::Sig storeExpressionResult = { { 739 /* storeExpressionResult */, 3, 18, 3, 21, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig narrowScopeWithCondition = { { 807 /* narrowScopeWithCondition */, 3, 22, 3, 25, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig processStmtNodes = { { 931 /* processStmtNodes */, 5, 26, 5, 31, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processLoopPassStmtNodes = { { 1005 /* processLoopPassStmtNodes */, 8, 32, 8, 40, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtNodesInternal = { { 1030 /* processStmtNodesInternal */, 6, 41, 6, 47, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtNode = { { 1080 /* processStmtNode */, 5, 48, 5, 53, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isAnalysedFile = { { 1105 /* isAnalysedFile */, 1, 54, 1, 55, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isReturningStoredExpressionResults = { { 1120 /* isReturningStoredExpressionResults */, 0, 56, 0, 56, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isConsumingStoredExpressionResults = { { 1155 /* isConsumingStoredExpressionResults */, 0, 57, 0, 57, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForSetAllowedUndefinedExpressions = { { 1190 /* lookForSetAllowedUndefinedExpressions */, 2, 58, 2, 60, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForUnsetAllowedUndefinedExpressions = { { 1228 /* lookForUnsetAllowedUndefinedExpressions */, 2, 61, 2, 63, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig lookForExpressionCallback = { { 1285 /* lookForExpressionCallback */, 3, 64, 3, 67, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processExprNodeConsumingStored = { { 1346 /* processExprNodeConsumingStored */, 6, 68, 6, 74, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprOnDemand = { { 1377 /* processExprOnDemand */, 3, 75, 3, 78, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readStoredResult = { { 1397 /* readStoredResult */, 2, 79, 2, 81, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readTypeOfMaybeStored = { { 1432 /* readTypeOfMaybeStored */, 2, 82, 2, 84, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findScopeStateType = { { 1454 /* findScopeStateType */, 2, 85, 2, 87, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig readScopeStateOrSyntheticType = { { 1473 /* readScopeStateOrSyntheticType */, 2, 88, 2, 90, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig requireScopeStateType = { { 1503 /* requireScopeStateType */, 2, 91, 2, 93, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig guardAgainstUnprocessedRealNode = { { 1532 /* guardAgainstUnprocessedRealNode */, 2, 94, 2, 96, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processSyntheticOnDemand = { { 1564 /* processSyntheticOnDemand */, 2, 97, 2, 99, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprNode = { { 1589 /* processExprNode */, 6, 100, 6, 106, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processExprNodeInternal = { { 1605 /* processExprNodeInternal */, 6, 107, 6, 113, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getImpurePointsFromPropertyHook = { { 1750 /* getImpurePointsFromPropertyHook */, 4, 114, 4, 118, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isPropertyHookBackingValueAccess = { { 1782 /* isPropertyHookBackingValueAccess */, 2, 119, 2, 121, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getAssignedVariables = { { 1815 /* getAssignedVariables */, 1, 122, 1, 123, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isReplayableConvergenceBody = { { 1855 /* isReplayableConvergenceBody */, 2, 124, 2, 126, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasContextSensitiveConstruct = { { 1888 /* hasContextSensitiveConstruct */, 1, 127, 1, 128, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig pushNodeGatherer = { { 1926 /* pushNodeGatherer */, 1, 129, 1, 130, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig popNodeGatherer = { { 1943 /* popNodeGatherer */, 0, 131, 0, 131, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig suspendNodeGatherers = { { 1959 /* suspendNodeGatherers */, 0, 132, 0, 132, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig restoreNodeGatherers = { { 1990 /* restoreNodeGatherers */, 1, 133, 1, 134, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig replayRecording = { { 2060 /* replayRecording */, 4, 135, 4, 139, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig replayRecordingRange = { { 2084 /* replayRecordingRange */, 6, 140, 6, 146, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig callNodeCallbackWithExpression = { { 2105 /* callNodeCallbackWithExpression */, 5, 147, 5, 152, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig callNodeCallback = { { 2136 /* callNodeCallback */, 4, 153, 4, 157, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig observingTemplateArgumentFrame = { { 2201 /* observingTemplateArgumentFrame */, 1, 158, 1, 159, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectReturnSend = { { 2301 /* collectReturnSend */, 2, 160, 2, 162, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectYieldSend = { { 2337 /* collectYieldSend */, 3, 163, 3, 166, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectYieldFromSend = { { 2368 /* collectYieldFromSend */, 2, 167, 2, 169, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDeclaredReturnType = { { 2389 /* getDeclaredReturnType */, 1, 170, 1, 171, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig processLoopFinalStmtNodes = { { 998 /* processLoopFinalStmtNodes */, 7, 32, 7, 39, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processLoopPassStmtNodes = { { 1040 /* processLoopPassStmtNodes */, 8, 40, 8, 48, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtNodesInternal = { { 1065 /* processStmtNodesInternal */, 6, 49, 6, 55, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtNode = { { 1115 /* processStmtNode */, 5, 56, 5, 61, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isAnalysedFile = { { 1140 /* isAnalysedFile */, 1, 62, 1, 63, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isReturningStoredExpressionResults = { { 1155 /* isReturningStoredExpressionResults */, 0, 64, 0, 64, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isConsumingStoredExpressionResults = { { 1190 /* isConsumingStoredExpressionResults */, 0, 65, 0, 65, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForSetAllowedUndefinedExpressions = { { 1225 /* lookForSetAllowedUndefinedExpressions */, 2, 66, 2, 68, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForUnsetAllowedUndefinedExpressions = { { 1263 /* lookForUnsetAllowedUndefinedExpressions */, 2, 69, 2, 71, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig lookForExpressionCallback = { { 1320 /* lookForExpressionCallback */, 3, 72, 3, 75, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processExprNodeConsumingStored = { { 1381 /* processExprNodeConsumingStored */, 6, 76, 6, 82, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprOnDemand = { { 1412 /* processExprOnDemand */, 3, 83, 3, 86, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readStoredResult = { { 1432 /* readStoredResult */, 2, 87, 2, 89, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readTypeOfMaybeStored = { { 1467 /* readTypeOfMaybeStored */, 2, 90, 2, 92, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findScopeStateType = { { 1489 /* findScopeStateType */, 2, 93, 2, 95, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig readScopeStateOrSyntheticType = { { 1508 /* readScopeStateOrSyntheticType */, 2, 96, 2, 98, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig requireScopeStateType = { { 1538 /* requireScopeStateType */, 2, 99, 2, 101, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig guardAgainstUnprocessedRealNode = { { 1567 /* guardAgainstUnprocessedRealNode */, 2, 102, 2, 104, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processSyntheticOnDemand = { { 1599 /* processSyntheticOnDemand */, 2, 105, 2, 107, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprNode = { { 1624 /* processExprNode */, 6, 108, 6, 114, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processExprNodeInternal = { { 1640 /* processExprNodeInternal */, 6, 115, 6, 121, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getImpurePointsFromPropertyHook = { { 1785 /* getImpurePointsFromPropertyHook */, 4, 122, 4, 126, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isPropertyHookBackingValueAccess = { { 1817 /* isPropertyHookBackingValueAccess */, 2, 127, 2, 129, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getAssignedVariables = { { 1850 /* getAssignedVariables */, 1, 130, 1, 131, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isReplayableConvergenceBody = { { 1890 /* isReplayableConvergenceBody */, 2, 132, 2, 134, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isContextSensitiveStatement = { { 1918 /* isContextSensitiveStatement */, 1, 135, 1, 136, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasContextSensitiveConstruct = { { 1951 /* hasContextSensitiveConstruct */, 1, 137, 1, 138, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig pushNodeGatherer = { { 1989 /* pushNodeGatherer */, 1, 139, 1, 140, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig popNodeGatherer = { { 2006 /* popNodeGatherer */, 0, 141, 0, 141, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig suspendNodeGatherers = { { 2022 /* suspendNodeGatherers */, 0, 142, 0, 142, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig restoreNodeGatherers = { { 2053 /* restoreNodeGatherers */, 1, 143, 1, 144, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig replayRecording = { { 2123 /* replayRecording */, 4, 145, 4, 149, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig replayRecordingRange = { { 2147 /* replayRecordingRange */, 6, 150, 6, 156, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callNodeCallbackWithExpression = { { 2168 /* callNodeCallbackWithExpression */, 5, 157, 5, 162, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig callNodeCallback = { { 2199 /* callNodeCallback */, 4, 163, 4, 167, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig observingTemplateArgumentFrame = { { 2264 /* observingTemplateArgumentFrame */, 1, 168, 1, 169, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectReturnSend = { { 2364 /* collectReturnSend */, 2, 170, 2, 172, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectYieldSend = { { 2400 /* collectYieldSend */, 3, 173, 3, 176, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectYieldFromSend = { { 2431 /* collectYieldFromSend */, 2, 177, 2, 179, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDeclaredReturnType = { { 2452 /* getDeclaredReturnType */, 1, 180, 1, 181, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; } // namespace sig } // namespace ptdecl::NodeScopeResolver diff --git a/turbo-ext/src/generated/StatementsHandler.h b/turbo-ext/src/generated/StatementsHandler.h index 13ab5d8c2cf..08f4e1b1eed 100644 --- a/turbo-ext/src/generated/StatementsHandler.h +++ b/turbo-ext/src/generated/StatementsHandler.h @@ -86,84 +86,89 @@ inline constexpr char strings[] = "previous\0" /* 963 */ "record\0" /* 972 */ "processLoopPassStmtNodes\0" /* 979 */ - "variableNames\0" /* 1004 */ - "withByRefClosureHolders\0" /* 1018 */ - "state\0" /* 1042 */ - "PHPStan\\Analyser\\StatementListWalkState\0" /* 1048 */ - "exitPointCount\0" /* 1088 */ - "throwPointCount\0" /* 1103 */ - "recordedEntry\0" /* 1119 */ - "rebaseAppendedPoints\0" /* 1133 */ - "i\0" /* 1154 */ - "stmt\0" /* 1156 */ - "PhpParser\\Node\\Stmt\0" /* 1161 */ - "shouldCheckLastStatement\0" /* 1181 */ - "processStatementStep\0" /* 1206 */ - "PHPStan\\Analyser\\ExpressionResult\0" /* 1227 */ - "readEndStatementExprResult\0" /* 1261 */ - "processBodyStmtNodesTwoPass\0" /* 1288 */ - "staticSites\0" /* 1316 */ - "staticStatementIndexes\0" /* 1328 */ - "parentFrame\0" /* 1351 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 1363 */ - "entries\0" /* 1411 */ - "recording\0" /* 1419 */ - "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 1429 */ - "inferStaticVariableTypes\0" /* 1468 */ - "staticWalk\0" /* 1493 */ - "frame\0" /* 1504 */ - "parentConstraints\0" /* 1510 */ - "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 1528 */ - "replayStaticVariableWalk\0" /* 1582 */ - "bodyScope\0" /* 1607 */ - "endState\0" /* 1617 */ - "moreScopes\0" /* 1626 */ - "collectStaticVariableScopes\0" /* 1637 */ - "collectStaticVariableStateScopes\0" /* 1665 */ - "names\0" /* 1698 */ - "scopes\0" /* 1704 */ - "collectStaticVariableTypes\0" /* 1711 */ - "runs\0" /* 1738 */ - "types\0" /* 1743 */ - "walkedConditionalExpressions\0" /* 1749 */ - "enteredTypes\0" /* 1778 */ - "collectStaticVariableConditionalExpressions\0" /* 1791 */ - "isWalkedStaticVariableState\0" /* 1835 */ - "a\0" /* 1863 */ - "b\0" /* 1865 */ - "equalStaticVariableStates\0" /* 1867 */ - "equalStaticVariableConditionalExpressions\0" /* 1893 */ - "PHPStan\\Analyser\\ConditionalExpressionHolder\0" /* 1935 */ - "equalConditionalExpressionHolders\0" /* 1980 */ - "walkTypes\0" /* 2014 */ - "generalize\0" /* 2024 */ - "joinStaticVariableTypes\0" /* 2035 */ - "PHPStan\\Analyser\\ClosureProcessor\0" /* 2059 */ - "getClosureProcessor\0" /* 2093 */ - "processDeferredByRefClosureBodies\0" /* 2113 */ - "statementStartTokenPositions\0" /* 2147 */ - "observeClosureSignatures\0" /* 2176 */ - "recordedExit\0" /* 2201 */ - "withRecordedConstraints\0" /* 2214 */ - "containsLabels\0" /* 2238 */ - "from\0" /* 2253 */ - "to\0" /* 2258 */ - "appendRecordedStatementResults\0" /* 2261 */ - "PHPStan\\Analyser\\VariableFlow\0" /* 2292 */ - "getVariableMentionFlow\0" /* 2322 */ - "statementMentionsAnyVariable\0" /* 2345 */ - "node\0" /* 2374 */ - "mentionsEverything\0" /* 2379 */ - "collectMentionedVariables\0" /* 2398 */ - "getVarTagDependencies\0" /* 2424 */ - "statement\0" /* 2446 */ - "getOverridingThrowPoints\0" /* 2456 */ - "defaultExpr\0" /* 2481 */ - "PhpParser\\Node\\Expr\0" /* 2493 */ - "processStmtVarAnnotation\0" /* 2513 */ - "PHPStan\\PhpDoc\\Tag\\VarTag\0" /* 2538 */ - "findSingleVariableLessVarTag\0" /* 2564 */ - "emitVarTagChangedNode"; /* 2593 */ + "lastPass\0" /* 1004 */ + "processLoopFinalStmtNodes\0" /* 1013 */ + "replayTarget\0" /* 1039 */ + "finalWalk\0" /* 1052 */ + "walkAgainstPreviousPass\0" /* 1062 */ + "variableNames\0" /* 1086 */ + "withByRefClosureHolders\0" /* 1100 */ + "state\0" /* 1124 */ + "PHPStan\\Analyser\\StatementListWalkState\0" /* 1130 */ + "exitPointCount\0" /* 1170 */ + "throwPointCount\0" /* 1185 */ + "recordedEntry\0" /* 1201 */ + "rebaseAppendedPoints\0" /* 1215 */ + "i\0" /* 1236 */ + "stmt\0" /* 1238 */ + "PhpParser\\Node\\Stmt\0" /* 1243 */ + "shouldCheckLastStatement\0" /* 1263 */ + "processStatementStep\0" /* 1288 */ + "PHPStan\\Analyser\\ExpressionResult\0" /* 1309 */ + "readEndStatementExprResult\0" /* 1343 */ + "processBodyStmtNodesTwoPass\0" /* 1370 */ + "staticSites\0" /* 1398 */ + "staticStatementIndexes\0" /* 1410 */ + "parentFrame\0" /* 1433 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentFrame\0" /* 1445 */ + "entries\0" /* 1493 */ + "recording\0" /* 1501 */ + "PHPStan\\Analyser\\RecordingNodeCallback\0" /* 1511 */ + "inferStaticVariableTypes\0" /* 1550 */ + "staticWalk\0" /* 1575 */ + "frame\0" /* 1586 */ + "parentConstraints\0" /* 1592 */ + "PHPStan\\Analyser\\Generics\\TemplateArgumentConstraints\0" /* 1610 */ + "replayStaticVariableWalk\0" /* 1664 */ + "bodyScope\0" /* 1689 */ + "endState\0" /* 1699 */ + "moreScopes\0" /* 1708 */ + "collectStaticVariableScopes\0" /* 1719 */ + "collectStaticVariableStateScopes\0" /* 1747 */ + "names\0" /* 1780 */ + "scopes\0" /* 1786 */ + "collectStaticVariableTypes\0" /* 1793 */ + "runs\0" /* 1820 */ + "types\0" /* 1825 */ + "walkedConditionalExpressions\0" /* 1831 */ + "enteredTypes\0" /* 1860 */ + "collectStaticVariableConditionalExpressions\0" /* 1873 */ + "isWalkedStaticVariableState\0" /* 1917 */ + "a\0" /* 1945 */ + "b\0" /* 1947 */ + "equalStaticVariableStates\0" /* 1949 */ + "equalStaticVariableConditionalExpressions\0" /* 1975 */ + "PHPStan\\Analyser\\ConditionalExpressionHolder\0" /* 2017 */ + "equalConditionalExpressionHolders\0" /* 2062 */ + "walkTypes\0" /* 2096 */ + "generalize\0" /* 2106 */ + "joinStaticVariableTypes\0" /* 2117 */ + "PHPStan\\Analyser\\ClosureProcessor\0" /* 2141 */ + "getClosureProcessor\0" /* 2175 */ + "processDeferredByRefClosureBodies\0" /* 2195 */ + "statementStartTokenPositions\0" /* 2229 */ + "observeClosureSignatures\0" /* 2258 */ + "recordedExit\0" /* 2283 */ + "withRecordedConstraints\0" /* 2296 */ + "containsLabels\0" /* 2320 */ + "from\0" /* 2335 */ + "to\0" /* 2340 */ + "appendRecordedStatementResults\0" /* 2343 */ + "PHPStan\\Analyser\\VariableFlow\0" /* 2374 */ + "getVariableMentionFlow\0" /* 2404 */ + "statementMentionsAnyVariable\0" /* 2427 */ + "node\0" /* 2456 */ + "mentionsEverything\0" /* 2461 */ + "collectMentionedVariables\0" /* 2480 */ + "getVarTagDependencies\0" /* 2506 */ + "statement\0" /* 2528 */ + "getOverridingThrowPoints\0" /* 2538 */ + "defaultExpr\0" /* 2563 */ + "PhpParser\\Node\\Expr\0" /* 2575 */ + "processStmtVarAnnotation\0" /* 2595 */ + "PHPStan\\PhpDoc\\Tag\\VarTag\0" /* 2620 */ + "findSingleVariableLessVarTag\0" /* 2646 */ + "emitVarTagChangedNode"; /* 2675 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 15), /* __construct $fileTypeMapper */ reg::packed(43, 0, 68), /* __construct $templateArgumentObserver */ @@ -218,28 +223,48 @@ inline constexpr reg::PackedArg args[] = { reg::packed(963, MAY_BE_NULL | MAY_BE_ARRAY), /* processLoopPassStmtNodes $previous */ reg::packed(972, MAY_BE_NULL | MAY_BE_ARRAY, reg::NoString, true, false), /* processLoopPassStmtNodes $record */ reg::packed(531, 0, 903), /* processLoopPassStmtNodes return */ + reg::packed(357, 0, 375), /* processLoopFinalStmtNodes $nodeScopeResolver */ + reg::packed(588, 0, 599), /* processLoopFinalStmtNodes $parentNode */ + reg::packed(897, MAY_BE_ARRAY), /* processLoopFinalStmtNodes $stmts */ + reg::packed(416, 0, 422), /* processLoopFinalStmtNodes $scope */ + reg::packed(624, 0, 477), /* processLoopFinalStmtNodes $storage */ + reg::packed(518, MAY_BE_CALLABLE), /* processLoopFinalStmtNodes $nodeCallback */ + reg::packed(632, 0, 640), /* processLoopFinalStmtNodes $context */ + reg::packed(1004, MAY_BE_ARRAY), /* processLoopFinalStmtNodes $lastPass */ + reg::packed(531, 0, 903), /* processLoopFinalStmtNodes return */ + reg::packed(357, 0, 375), /* walkAgainstPreviousPass $nodeScopeResolver */ + reg::packed(588, 0, 599), /* walkAgainstPreviousPass $parentNode */ + reg::packed(897, MAY_BE_ARRAY), /* walkAgainstPreviousPass $stmts */ + reg::packed(416, 0, 422), /* walkAgainstPreviousPass $scope */ + reg::packed(624, 0, 477), /* walkAgainstPreviousPass $storage */ + reg::packed(518, MAY_BE_CALLABLE), /* walkAgainstPreviousPass $nodeCallback */ + reg::packed(632, 0, 640), /* walkAgainstPreviousPass $context */ + reg::packed(963, MAY_BE_NULL | MAY_BE_ARRAY), /* walkAgainstPreviousPass $previous */ + reg::packed(1039, MAY_BE_NULL | MAY_BE_CALLABLE), /* walkAgainstPreviousPass $replayTarget */ + reg::packed(1052, MAY_BE_BOOL), /* walkAgainstPreviousPass $finalWalk */ + reg::packed(531, MAY_BE_ARRAY), /* walkAgainstPreviousPass return */ reg::packed(416, 0, 422), /* withByRefClosureHolders $scope */ - reg::packed(1004, MAY_BE_ARRAY), /* withByRefClosureHolders $variableNames */ + reg::packed(1086, MAY_BE_ARRAY), /* withByRefClosureHolders $variableNames */ reg::packed(531, MAY_BE_ARRAY), /* withByRefClosureHolders return */ - reg::packed(1042, 0, 1048), /* rebaseAppendedPoints $state */ - reg::packed(1088, MAY_BE_LONG), /* rebaseAppendedPoints $exitPointCount */ - reg::packed(1103, MAY_BE_LONG), /* rebaseAppendedPoints $throwPointCount */ - reg::packed(1119, 0, 422), /* rebaseAppendedPoints $recordedEntry */ + reg::packed(1124, 0, 1130), /* rebaseAppendedPoints $state */ + reg::packed(1170, MAY_BE_LONG), /* rebaseAppendedPoints $exitPointCount */ + reg::packed(1185, MAY_BE_LONG), /* rebaseAppendedPoints $throwPointCount */ + reg::packed(1201, 0, 422), /* rebaseAppendedPoints $recordedEntry */ reg::packed(531, MAY_BE_VOID), /* rebaseAppendedPoints return */ reg::packed(357, 0, 375), /* processStatementStep $nodeScopeResolver */ reg::packed(588, 0, 599), /* processStatementStep $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* processStatementStep $stmts */ - reg::packed(1154, MAY_BE_LONG), /* processStatementStep $i */ - reg::packed(1156, 0, 1161), /* processStatementStep $stmt */ - reg::packed(1042, 0, 1048), /* processStatementStep $state */ + reg::packed(1236, MAY_BE_LONG), /* processStatementStep $i */ + reg::packed(1238, 0, 1243), /* processStatementStep $stmt */ + reg::packed(1124, 0, 1130), /* processStatementStep $state */ reg::packed(624, 0, 477), /* processStatementStep $storage */ reg::packed(518, MAY_BE_CALLABLE), /* processStatementStep $nodeCallback */ reg::packed(632, 0, 640), /* processStatementStep $context */ - reg::packed(1181, MAY_BE_BOOL), /* processStatementStep $shouldCheckLastStatement */ + reg::packed(1263, MAY_BE_BOOL), /* processStatementStep $shouldCheckLastStatement */ reg::packed(531, MAY_BE_VOID), /* processStatementStep return */ - reg::packed(1156, 0, 1161), /* readEndStatementExprResult $stmt */ + reg::packed(1238, 0, 1243), /* readEndStatementExprResult $stmt */ reg::packed(624, 0, 477), /* readEndStatementExprResult $storage */ - reg::packed(531, MAY_BE_NULL, 1227), /* readEndStatementExprResult return */ + reg::packed(531, MAY_BE_NULL, 1309), /* readEndStatementExprResult return */ reg::packed(357, 0, 375), /* processBodyStmtNodesTwoPass $nodeScopeResolver */ reg::packed(588, 0, 599), /* processBodyStmtNodesTwoPass $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* processBodyStmtNodesTwoPass $stmts */ @@ -251,124 +276,124 @@ inline constexpr reg::PackedArg args[] = { reg::packed(357, 0, 375), /* inferStaticVariableTypes $nodeScopeResolver */ reg::packed(588, 0, 599), /* inferStaticVariableTypes $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* inferStaticVariableTypes $stmts */ - reg::packed(1316, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticSites */ - reg::packed(1328, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticStatementIndexes */ - reg::packed(1351, MAY_BE_NULL, 1363), /* inferStaticVariableTypes $parentFrame */ - reg::packed(1411, MAY_BE_ARRAY), /* inferStaticVariableTypes $entries */ - reg::packed(1419, 0, 1429), /* inferStaticVariableTypes $recording */ + reg::packed(1398, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticSites */ + reg::packed(1410, MAY_BE_ARRAY), /* inferStaticVariableTypes $staticStatementIndexes */ + reg::packed(1433, MAY_BE_NULL, 1445), /* inferStaticVariableTypes $parentFrame */ + reg::packed(1493, MAY_BE_ARRAY), /* inferStaticVariableTypes $entries */ + reg::packed(1501, 0, 1511), /* inferStaticVariableTypes $recording */ reg::packed(624, 0, 477), /* inferStaticVariableTypes $storage */ reg::packed(632, 0, 640), /* inferStaticVariableTypes $context */ reg::packed(531, MAY_BE_ARRAY), /* inferStaticVariableTypes return */ reg::packed(357, 0, 375), /* replayStaticVariableWalk $nodeScopeResolver */ reg::packed(897, MAY_BE_ARRAY), /* replayStaticVariableWalk $stmts */ - reg::packed(1411, MAY_BE_ARRAY), /* replayStaticVariableWalk $entries */ - reg::packed(1419, 0, 1429), /* replayStaticVariableWalk $recording */ - reg::packed(1493, MAY_BE_ARRAY), /* replayStaticVariableWalk $staticWalk */ - reg::packed(1504, 0, 1363), /* replayStaticVariableWalk $frame */ - reg::packed(1351, MAY_BE_NULL, 1363), /* replayStaticVariableWalk $parentFrame */ - reg::packed(1510, MAY_BE_NULL, 1528), /* replayStaticVariableWalk $parentConstraints */ + reg::packed(1493, MAY_BE_ARRAY), /* replayStaticVariableWalk $entries */ + reg::packed(1501, 0, 1511), /* replayStaticVariableWalk $recording */ + reg::packed(1575, MAY_BE_ARRAY), /* replayStaticVariableWalk $staticWalk */ + reg::packed(1586, 0, 1445), /* replayStaticVariableWalk $frame */ + reg::packed(1433, MAY_BE_NULL, 1445), /* replayStaticVariableWalk $parentFrame */ + reg::packed(1592, MAY_BE_NULL, 1610), /* replayStaticVariableWalk $parentConstraints */ reg::packed(624, 0, 477), /* replayStaticVariableWalk $storage */ reg::packed(518, MAY_BE_CALLABLE), /* replayStaticVariableWalk $nodeCallback */ reg::packed(416, 0, 422), /* replayStaticVariableWalk $scope */ reg::packed(531, 0, 903), /* replayStaticVariableWalk return */ - reg::packed(1607, 0, 422), /* collectStaticVariableScopes $bodyScope */ - reg::packed(1419, 0, 1429), /* collectStaticVariableScopes $recording */ - reg::packed(1617, 0, 1048), /* collectStaticVariableScopes $endState */ - reg::packed(1626, MAY_BE_ARRAY), /* collectStaticVariableScopes $moreScopes */ + reg::packed(1689, 0, 422), /* collectStaticVariableScopes $bodyScope */ + reg::packed(1501, 0, 1511), /* collectStaticVariableScopes $recording */ + reg::packed(1699, 0, 1130), /* collectStaticVariableScopes $endState */ + reg::packed(1708, MAY_BE_ARRAY), /* collectStaticVariableScopes $moreScopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableScopes return */ - reg::packed(1607, 0, 422), /* collectStaticVariableStateScopes $bodyScope */ - reg::packed(1419, 0, 1429), /* collectStaticVariableStateScopes $recording */ - reg::packed(1617, 0, 1048), /* collectStaticVariableStateScopes $endState */ - reg::packed(1626, MAY_BE_ARRAY), /* collectStaticVariableStateScopes $moreScopes */ + reg::packed(1689, 0, 422), /* collectStaticVariableStateScopes $bodyScope */ + reg::packed(1501, 0, 1511), /* collectStaticVariableStateScopes $recording */ + reg::packed(1699, 0, 1130), /* collectStaticVariableStateScopes $endState */ + reg::packed(1708, MAY_BE_ARRAY), /* collectStaticVariableStateScopes $moreScopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableStateScopes return */ - reg::packed(1698, MAY_BE_ARRAY), /* collectStaticVariableTypes $names */ - reg::packed(1704, MAY_BE_ARRAY), /* collectStaticVariableTypes $scopes */ + reg::packed(1780, MAY_BE_ARRAY), /* collectStaticVariableTypes $names */ + reg::packed(1786, MAY_BE_ARRAY), /* collectStaticVariableTypes $scopes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableTypes return */ - reg::packed(1738, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $runs */ - reg::packed(1704, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $scopes */ + reg::packed(1820, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $runs */ + reg::packed(1786, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $scopes */ reg::packed(624, 0, 477), /* collectStaticVariableConditionalExpressions $storage */ - reg::packed(1743, MAY_BE_NULL | MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $types */ - reg::packed(1749, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $walkedConditionalExpressions */ - reg::packed(1778, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $enteredTypes */ + reg::packed(1825, MAY_BE_NULL | MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $types */ + reg::packed(1831, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $walkedConditionalExpressions */ + reg::packed(1860, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions $enteredTypes */ reg::packed(531, MAY_BE_ARRAY), /* collectStaticVariableConditionalExpressions return */ - reg::packed(1042, MAY_BE_ARRAY), /* isWalkedStaticVariableState $state */ - reg::packed(1743, MAY_BE_ARRAY), /* isWalkedStaticVariableState $types */ + reg::packed(1124, MAY_BE_ARRAY), /* isWalkedStaticVariableState $state */ + reg::packed(1825, MAY_BE_ARRAY), /* isWalkedStaticVariableState $types */ reg::packed(416, 0, 422), /* isWalkedStaticVariableState $scope */ - reg::packed(1749, MAY_BE_ARRAY), /* isWalkedStaticVariableState $walkedConditionalExpressions */ + reg::packed(1831, MAY_BE_ARRAY), /* isWalkedStaticVariableState $walkedConditionalExpressions */ reg::packed(531, MAY_BE_BOOL), /* isWalkedStaticVariableState return */ - reg::packed(1863, MAY_BE_ARRAY), /* equalStaticVariableStates $a */ - reg::packed(1865, MAY_BE_ARRAY), /* equalStaticVariableStates $b */ + reg::packed(1945, MAY_BE_ARRAY), /* equalStaticVariableStates $a */ + reg::packed(1947, MAY_BE_ARRAY), /* equalStaticVariableStates $b */ reg::packed(531, MAY_BE_BOOL), /* equalStaticVariableStates return */ - reg::packed(1863, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $a */ - reg::packed(1865, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $b */ + reg::packed(1945, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $a */ + reg::packed(1947, MAY_BE_ARRAY), /* equalStaticVariableConditionalExpressions $b */ reg::packed(531, MAY_BE_BOOL), /* equalStaticVariableConditionalExpressions return */ - reg::packed(1863, 0, 1935), /* equalConditionalExpressionHolders $a */ - reg::packed(1865, 0, 1935), /* equalConditionalExpressionHolders $b */ + reg::packed(1945, 0, 2017), /* equalConditionalExpressionHolders $a */ + reg::packed(1947, 0, 2017), /* equalConditionalExpressionHolders $b */ reg::packed(531, MAY_BE_BOOL), /* equalConditionalExpressionHolders return */ - reg::packed(1607, 0, 422), /* joinStaticVariableTypes $bodyScope */ - reg::packed(1698, MAY_BE_ARRAY), /* joinStaticVariableTypes $names */ - reg::packed(1743, MAY_BE_ARRAY), /* joinStaticVariableTypes $types */ - reg::packed(2014, MAY_BE_ARRAY), /* joinStaticVariableTypes $walkTypes */ - reg::packed(2024, MAY_BE_BOOL), /* joinStaticVariableTypes $generalize */ + reg::packed(1689, 0, 422), /* joinStaticVariableTypes $bodyScope */ + reg::packed(1780, MAY_BE_ARRAY), /* joinStaticVariableTypes $names */ + reg::packed(1825, MAY_BE_ARRAY), /* joinStaticVariableTypes $types */ + reg::packed(2096, MAY_BE_ARRAY), /* joinStaticVariableTypes $walkTypes */ + reg::packed(2106, MAY_BE_BOOL), /* joinStaticVariableTypes $generalize */ reg::packed(531, MAY_BE_ARRAY), /* joinStaticVariableTypes return */ - reg::packed(531, 0, 2059), /* getClosureProcessor return */ + reg::packed(531, 0, 2141), /* getClosureProcessor return */ reg::packed(357, 0, 375), /* processDeferredByRefClosureBodies $nodeScopeResolver */ - reg::packed(1504, 0, 1363), /* processDeferredByRefClosureBodies $frame */ - reg::packed(1042, 0, 1048), /* processDeferredByRefClosureBodies $state */ + reg::packed(1586, 0, 1445), /* processDeferredByRefClosureBodies $frame */ + reg::packed(1124, 0, 1130), /* processDeferredByRefClosureBodies $state */ reg::packed(624, 0, 477), /* processDeferredByRefClosureBodies $storage */ reg::packed(518, MAY_BE_CALLABLE), /* processDeferredByRefClosureBodies $nodeCallback */ reg::packed(531, MAY_BE_VOID), /* processDeferredByRefClosureBodies return */ reg::packed(357, 0, 375), /* observeClosureSignatures $nodeScopeResolver */ reg::packed(588, 0, 599), /* observeClosureSignatures $parentNode */ reg::packed(897, MAY_BE_ARRAY), /* observeClosureSignatures $stmts */ - reg::packed(1504, 0, 1363), /* observeClosureSignatures $frame */ - reg::packed(1411, MAY_BE_ARRAY), /* observeClosureSignatures $entries */ + reg::packed(1586, 0, 1445), /* observeClosureSignatures $frame */ + reg::packed(1493, MAY_BE_ARRAY), /* observeClosureSignatures $entries */ reg::packed(624, 0, 477), /* observeClosureSignatures $storage */ reg::packed(632, 0, 640), /* observeClosureSignatures $context */ - reg::packed(2147, MAY_BE_ARRAY), /* observeClosureSignatures $statementStartTokenPositions */ - reg::packed(531, 0, 1363), /* observeClosureSignatures return */ + reg::packed(2229, MAY_BE_ARRAY), /* observeClosureSignatures $statementStartTokenPositions */ + reg::packed(531, 0, 1445), /* observeClosureSignatures return */ reg::packed(416, 0, 422), /* withRecordedConstraints $scope */ - reg::packed(1119, 0, 422), /* withRecordedConstraints $recordedEntry */ - reg::packed(2201, 0, 422), /* withRecordedConstraints $recordedExit */ + reg::packed(1201, 0, 422), /* withRecordedConstraints $recordedEntry */ + reg::packed(2283, 0, 422), /* withRecordedConstraints $recordedExit */ reg::packed(531, 0, 422), /* withRecordedConstraints return */ reg::packed(897, MAY_BE_ARRAY), /* containsLabels $stmts */ reg::packed(531, MAY_BE_BOOL), /* containsLabels return */ - reg::packed(1042, 0, 1048), /* appendRecordedStatementResults $state */ - reg::packed(2253, 0, 1048), /* appendRecordedStatementResults $from */ - reg::packed(2258, 0, 1048), /* appendRecordedStatementResults $to */ + reg::packed(1124, 0, 1130), /* appendRecordedStatementResults $state */ + reg::packed(2335, 0, 1130), /* appendRecordedStatementResults $from */ + reg::packed(2340, 0, 1130), /* appendRecordedStatementResults $to */ reg::packed(531, MAY_BE_VOID), /* appendRecordedStatementResults return */ - reg::packed(1156, 0, 1161), /* getVariableMentionFlow $stmt */ - reg::packed(531, MAY_BE_NULL, 2292), /* getVariableMentionFlow return */ - reg::packed(1156, 0, 1161), /* statementMentionsAnyVariable $stmt */ - reg::packed(1004, MAY_BE_ARRAY), /* statementMentionsAnyVariable $variableNames */ + reg::packed(1238, 0, 1243), /* getVariableMentionFlow $stmt */ + reg::packed(531, MAY_BE_NULL, 2374), /* getVariableMentionFlow return */ + reg::packed(1238, 0, 1243), /* statementMentionsAnyVariable $stmt */ + reg::packed(1086, MAY_BE_ARRAY), /* statementMentionsAnyVariable $variableNames */ reg::packed(531, MAY_BE_BOOL), /* statementMentionsAnyVariable return */ - reg::packed(2374, 0, 599), /* collectMentionedVariables $node */ - reg::packed(1698, MAY_BE_ARRAY, reg::NoString, true, false), /* collectMentionedVariables $names */ - reg::packed(2379, MAY_BE_BOOL, reg::NoString, true, false), /* collectMentionedVariables $mentionsEverything */ + reg::packed(2456, 0, 599), /* collectMentionedVariables $node */ + reg::packed(1780, MAY_BE_ARRAY, reg::NoString, true, false), /* collectMentionedVariables $names */ + reg::packed(2461, MAY_BE_BOOL, reg::NoString, true, false), /* collectMentionedVariables $mentionsEverything */ reg::packed(531, MAY_BE_VOID), /* collectMentionedVariables return */ - reg::packed(1156, 0, 1161), /* getVarTagDependencies $stmt */ + reg::packed(1238, 0, 1243), /* getVarTagDependencies $stmt */ reg::packed(416, 0, 422), /* getVarTagDependencies $scope */ reg::packed(531, MAY_BE_NULL, 532), /* getVarTagDependencies return */ - reg::packed(2446, 0, 1161), /* getOverridingThrowPoints $statement */ + reg::packed(2528, 0, 1243), /* getOverridingThrowPoints $statement */ reg::packed(416, 0, 422), /* getOverridingThrowPoints $scope */ reg::packed(531, MAY_BE_NULL | MAY_BE_ARRAY), /* getOverridingThrowPoints return */ reg::packed(357, 0, 375), /* processStmtVarAnnotation $nodeScopeResolver */ reg::packed(416, 0, 422), /* processStmtVarAnnotation $scope */ reg::packed(624, 0, 477), /* processStmtVarAnnotation $storage */ - reg::packed(1156, 0, 1161), /* processStmtVarAnnotation $stmt */ - reg::packed(2481, MAY_BE_NULL, 2493), /* processStmtVarAnnotation $defaultExpr */ + reg::packed(1238, 0, 1243), /* processStmtVarAnnotation $stmt */ + reg::packed(2563, MAY_BE_NULL, 2575), /* processStmtVarAnnotation $defaultExpr */ reg::packed(518, MAY_BE_CALLABLE), /* processStmtVarAnnotation $nodeCallback */ reg::packed(531, 0, 422), /* processStmtVarAnnotation return */ reg::packed(416, 0, 422), /* findSingleVariableLessVarTag $scope */ - reg::packed(1156, 0, 1161), /* findSingleVariableLessVarTag $stmt */ - reg::packed(531, MAY_BE_NULL, 2538), /* findSingleVariableLessVarTag return */ + reg::packed(1238, 0, 1243), /* findSingleVariableLessVarTag $stmt */ + reg::packed(531, MAY_BE_NULL, 2620), /* findSingleVariableLessVarTag return */ reg::packed(357, 0, 375), /* emitVarTagChangedNode $nodeScopeResolver */ reg::packed(416, 0, 422), /* emitVarTagChangedNode $scope */ reg::packed(624, 0, 477), /* emitVarTagChangedNode $storage */ - reg::packed(1156, 0, 1161), /* emitVarTagChangedNode $stmt */ - reg::packed(2481, 0, 2493), /* emitVarTagChangedNode $defaultExpr */ + reg::packed(1238, 0, 1243), /* emitVarTagChangedNode $stmt */ + reg::packed(2563, 0, 2575), /* emitVarTagChangedNode $defaultExpr */ reg::packed(518, MAY_BE_CALLABLE), /* emitVarTagChangedNode $nodeCallback */ - reg::packed(531, 0, 1528), /* emitVarTagChangedNode return */ + reg::packed(531, 0, 1610), /* emitVarTagChangedNode return */ }; using Sig = reg::Sig; } // namespace sigtab @@ -383,36 +408,38 @@ inline constexpr sigtab::Sig processUnreachableStatement = { { 827 /* processUnr inline constexpr sigtab::Sig getNextUnreachableStatements = { { 868 /* getNextUnreachableStatements */, 2, 32, 2, 34, ZEND_ACC_PRIVATE } }; inline constexpr sigtab::Sig doProcessStmtNodes = { { 944 /* doProcessStmtNodes */, 7, 35, 7, 42, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig processLoopPassStmtNodes = { { 979 /* processLoopPassStmtNodes */, 9, 43, 9, 52, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig withByRefClosureHolders = { { 1018 /* withByRefClosureHolders */, 2, 53, 2, 55, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig rebaseAppendedPoints = { { 1133 /* rebaseAppendedPoints */, 4, 56, 4, 60, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processStatementStep = { { 1206 /* processStatementStep */, 10, 61, 10, 71, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig readEndStatementExprResult = { { 1261 /* readEndStatementExprResult */, 2, 72, 2, 74, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processBodyStmtNodesTwoPass = { { 1288 /* processBodyStmtNodesTwoPass */, 7, 75, 7, 82, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig inferStaticVariableTypes = { { 1468 /* inferStaticVariableTypes */, 10, 83, 10, 93, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig replayStaticVariableWalk = { { 1582 /* replayStaticVariableWalk */, 11, 94, 11, 105, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableScopes = { { 1637 /* collectStaticVariableScopes */, 4, 106, 4, 110, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableStateScopes = { { 1665 /* collectStaticVariableStateScopes */, 4, 111, 4, 115, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableTypes = { { 1711 /* collectStaticVariableTypes */, 2, 116, 2, 118, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectStaticVariableConditionalExpressions = { { 1791 /* collectStaticVariableConditionalExpressions */, 6, 119, 6, 125, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isWalkedStaticVariableState = { { 1835 /* isWalkedStaticVariableState */, 4, 126, 4, 130, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalStaticVariableStates = { { 1867 /* equalStaticVariableStates */, 2, 131, 2, 133, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalStaticVariableConditionalExpressions = { { 1893 /* equalStaticVariableConditionalExpressions */, 2, 134, 2, 136, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig equalConditionalExpressionHolders = { { 1980 /* equalConditionalExpressionHolders */, 2, 137, 2, 139, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig joinStaticVariableTypes = { { 2035 /* joinStaticVariableTypes */, 5, 140, 5, 145, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getClosureProcessor = { { 2093 /* getClosureProcessor */, 0, 146, 0, 146, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig processDeferredByRefClosureBodies = { { 2113 /* processDeferredByRefClosureBodies */, 5, 147, 5, 152, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig observeClosureSignatures = { { 2176 /* observeClosureSignatures */, 8, 153, 8, 161, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig withRecordedConstraints = { { 2214 /* withRecordedConstraints */, 3, 162, 3, 165, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig containsLabels = { { 2238 /* containsLabels */, 1, 166, 1, 167, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig appendRecordedStatementResults = { { 2261 /* appendRecordedStatementResults */, 3, 168, 3, 171, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getVariableMentionFlow = { { 2322 /* getVariableMentionFlow */, 1, 172, 1, 173, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig statementMentionsAnyVariable = { { 2345 /* statementMentionsAnyVariable */, 2, 174, 2, 176, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig collectMentionedVariables = { { 2398 /* collectMentionedVariables */, 3, 177, 3, 180, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getVarTagDependencies = { { 2424 /* getVarTagDependencies */, 2, 181, 2, 183, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getOverridingThrowPoints = { { 2456 /* getOverridingThrowPoints */, 2, 184, 2, 186, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmtVarAnnotation = { { 2513 /* processStmtVarAnnotation */, 6, 187, 6, 193, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findSingleVariableLessVarTag = { { 2564 /* findSingleVariableLessVarTag */, 2, 194, 2, 196, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig emitVarTagChangedNode = { { 2593 /* emitVarTagChangedNode */, 6, 197, 6, 203, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processLoopFinalStmtNodes = { { 1013 /* processLoopFinalStmtNodes */, 8, 53, 8, 61, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig walkAgainstPreviousPass = { { 1062 /* walkAgainstPreviousPass */, 10, 62, 10, 72, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig withByRefClosureHolders = { { 1100 /* withByRefClosureHolders */, 2, 73, 2, 75, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig rebaseAppendedPoints = { { 1215 /* rebaseAppendedPoints */, 4, 76, 4, 80, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processStatementStep = { { 1288 /* processStatementStep */, 10, 81, 10, 91, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig readEndStatementExprResult = { { 1343 /* readEndStatementExprResult */, 2, 92, 2, 94, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processBodyStmtNodesTwoPass = { { 1370 /* processBodyStmtNodesTwoPass */, 7, 95, 7, 102, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig inferStaticVariableTypes = { { 1550 /* inferStaticVariableTypes */, 10, 103, 10, 113, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig replayStaticVariableWalk = { { 1664 /* replayStaticVariableWalk */, 11, 114, 11, 125, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableScopes = { { 1719 /* collectStaticVariableScopes */, 4, 126, 4, 130, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableStateScopes = { { 1747 /* collectStaticVariableStateScopes */, 4, 131, 4, 135, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableTypes = { { 1793 /* collectStaticVariableTypes */, 2, 136, 2, 138, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectStaticVariableConditionalExpressions = { { 1873 /* collectStaticVariableConditionalExpressions */, 6, 139, 6, 145, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isWalkedStaticVariableState = { { 1917 /* isWalkedStaticVariableState */, 4, 146, 4, 150, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalStaticVariableStates = { { 1949 /* equalStaticVariableStates */, 2, 151, 2, 153, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalStaticVariableConditionalExpressions = { { 1975 /* equalStaticVariableConditionalExpressions */, 2, 154, 2, 156, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig equalConditionalExpressionHolders = { { 2062 /* equalConditionalExpressionHolders */, 2, 157, 2, 159, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig joinStaticVariableTypes = { { 2117 /* joinStaticVariableTypes */, 5, 160, 5, 165, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getClosureProcessor = { { 2175 /* getClosureProcessor */, 0, 166, 0, 166, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig processDeferredByRefClosureBodies = { { 2195 /* processDeferredByRefClosureBodies */, 5, 167, 5, 172, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig observeClosureSignatures = { { 2258 /* observeClosureSignatures */, 8, 173, 8, 181, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig withRecordedConstraints = { { 2296 /* withRecordedConstraints */, 3, 182, 3, 185, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig containsLabels = { { 2320 /* containsLabels */, 1, 186, 1, 187, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig appendRecordedStatementResults = { { 2343 /* appendRecordedStatementResults */, 3, 188, 3, 191, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getVariableMentionFlow = { { 2404 /* getVariableMentionFlow */, 1, 192, 1, 193, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig statementMentionsAnyVariable = { { 2427 /* statementMentionsAnyVariable */, 2, 194, 2, 196, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig collectMentionedVariables = { { 2480 /* collectMentionedVariables */, 3, 197, 3, 200, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getVarTagDependencies = { { 2506 /* getVarTagDependencies */, 2, 201, 2, 203, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOverridingThrowPoints = { { 2538 /* getOverridingThrowPoints */, 2, 204, 2, 206, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmtVarAnnotation = { { 2595 /* processStmtVarAnnotation */, 6, 207, 6, 213, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findSingleVariableLessVarTag = { { 2646 /* findSingleVariableLessVarTag */, 2, 214, 2, 216, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig emitVarTagChangedNode = { { 2675 /* emitVarTagChangedNode */, 6, 217, 6, 223, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::StatementsHandler diff --git a/turbo-ext/src/support.h b/turbo-ext/src/support.h index 128bc993851..2a07789bc6a 100644 --- a/turbo-ext/src/support.h +++ b/turbo-ext/src/support.h @@ -2280,6 +2280,8 @@ zv::Val pt_node_scope_resolver_process_stmt_nodes_internal(zval *nodeScopeResolv /* $nodeScopeResolver->processLoopPassStmtNodes(...); previous IS_NULL for * the first pass, record receives this pass's [entries, recording] */ zv::Val pt_node_scope_resolver_process_loop_pass_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record); +zv::Val pt_node_scope_resolver_process_loop_final_stmt_nodes(zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass); +[[nodiscard]] bool pt_node_scope_resolver_is_context_sensitive_statement(zval *nodeScopeResolver, zval *stmt, bool &out); zv::Val pt_node_scope_resolver_process_expr_on_demand(zval *nodeScopeResolver, zval *expr, zval *scope, zval *storage); zv::Val pt_node_scope_resolver_process_synthetic_on_demand(zval *nodeScopeResolver, zval *expr, zval *scope); zv::Val pt_node_scope_resolver_find_scope_state_type(zval *nodeScopeResolver, zval *expr, zval *scope); @@ -2310,6 +2312,7 @@ zv::Val pt_statements_handler_do_process_stmt_nodes(zval *handler, zval *nodeSco * only what differs from $previous (IS_NULL for the first pass); record * receives this pass's [entries, recording]. UNDEF = pending exception */ zv::Val pt_statements_handler_process_loop_pass_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *previous, zv::Val &record); +zv::Val pt_statements_handler_process_loop_final_stmt_nodes(zval *handler, zval *nodeScopeResolver, zval *parentNode, zval *stmts, zval *scope, zval *storage, zval *nodeCallback, zval *context, zval *lastPass); zv::Val pt_statements_handler_process_stmt_var_annotation(zval *handler, zval *nodeScopeResolver, zval *scope, zval *storage, zval *stmt, zval *defaultExpr, zval *nodeCallback); zv::Val pt_statements_handler_get_overriding_throw_points(zval *handler, zval *statement, zval *scope); zv::Val pt_statements_handler_emit_var_tag_changed_node(zval *handler, zval *nodeScopeResolver, zval *scope, zval *storage, zval *stmt, zval *defaultExpr, zval *nodeCallback); From 09f25c7435e839035385365ff1899b6be7b95d72 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:11 +0200 Subject: [PATCH 4/8] Skip re-pricing a binary operation on the operand types the walk saw InvalidBinaryOperationRule priced every binary operation and compound assignment a second time: it cloned the node with both operands replaced by TypeExpr of the types to check and walked that synthetic node on demand - three walks per operation, half of all on-demand walks in self-analysis. When the types to check are the operand types the walk used, the operation's own stored type answers the same question. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- src/Rules/Operators/InvalidBinaryOperationRule.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Rules/Operators/InvalidBinaryOperationRule.php b/src/Rules/Operators/InvalidBinaryOperationRule.php index 4141864d6fc..8786a69ced6 100644 --- a/src/Rules/Operators/InvalidBinaryOperationRule.php +++ b/src/Rules/Operators/InvalidBinaryOperationRule.php @@ -90,6 +90,16 @@ public function processNode(Node $node, Scope $scope): array return []; } + // on the very operand types the walk saw, the operation's own type is the + // answer - the walk already priced it + if ( + $leftType->equals($scope->getType($left)) + && $rightType->equals($scope->getType($right)) + && !$scope->getType($node) instanceof ErrorType + ) { + return []; + } + if ($node instanceof Node\Expr\AssignOp) { $newNode = clone $node; $newNode->setAttribute('phpstan_cache_printer', null); From eed1872dc14f611f8fa820f9394334addca6530a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:11 +0200 Subject: [PATCH 5/8] Compose the post-foreach empty-or-object narrowing instead of walking it After a foreach that may not iterate, the entry scope narrowed by `$iteratee === [] || is_object($iteratee)` merges into the exit scope. The condition was built as a synthetic BooleanOr and walked on demand, an is_object() call resolution included - 16K on-demand walks in self-analysis. ForeachEmptyOrObjectNarrowingHelper composes the same narrowing through BooleanNarrowingHelper::specifyDisjunction(): the comparison from the iteratee's result, is_object() from its conditional return type. The walk remains the fallback when a side cannot be composed. The helper runs once per loop exit and stays PHP; the native ForeachHandler calls it. Before switching, both were computed and compared for every narrowing in self-analysis and the test suite: equal scopes throughout, none falling back to the walk. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- .../ForeachEmptyOrObjectNarrowingHelper.php | 154 ++++++++++++++++++ src/Analyser/StmtHandler/ForeachHandler.php | 15 +- turbo-ext/src/ForeachHandler.cpp | 43 ++--- 3 files changed, 168 insertions(+), 44 deletions(-) create mode 100644 src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php diff --git a/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php b/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php new file mode 100644 index 00000000000..63decb2ed4e --- /dev/null +++ b/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php @@ -0,0 +1,154 @@ +composeScope($nodeScopeResolver, $scope, $condition, $identical, $isObjectCall, $iterateeResult, $storage) + ?? $nodeScopeResolver->narrowScopeWithCondition($scope, $condition, TypeSpecifierContext::createTruthy()); + } + + private function composeScope( + NodeScopeResolver $nodeScopeResolver, + MutatingScope $scope, + BooleanOr $condition, + Identical $identical, + FuncCall $isObjectCall, + ExpressionResult $iterateeResult, + ExpressionResultStorage $storage, + ): ?MutatingScope + { + $isObjectName = new FullyQualified('is_object'); + if (!$this->reflectionProvider->hasFunction($isObjectName, $scope)) { + return null; + } + $isObjectFunction = $this->reflectionProvider->getFunction($isObjectName, $scope); + $identicalNarrowingHelper = $this->identicalNarrowingHelper; + $defaultNarrowingHelper = $this->defaultNarrowingHelper; + $emptyArrayType = new ConstantArrayType([], []); + $subjectArgResult = $identicalNarrowingHelper->captureFirstArgResult($identical->left, $storage); + + $leftTypes = static fn (MutatingScope $s, TypeSpecifierContext $context): ?SpecifiedTypes => $identicalNarrowingHelper->specifyIdenticalAgainstType( + $identical->left, + $iterateeResult, + $identical->right, + $emptyArrayType, + $context, + $s, + $subjectArgResult, + static fn (): Type => self::emptyArrayComparison($iterateeResult->getType(), $emptyArrayType), + ); + $rightTypes = static function (MutatingScope $s, TypeSpecifierContext $context) use ($defaultNarrowingHelper, $isObjectCall, $isObjectFunction): ?SpecifiedTypes { + $acceptor = ParametersAcceptorSelector::selectFromArgs($s, $isObjectCall->getArgs(), $isObjectFunction->getVariants(), $isObjectFunction->getNamedArgumentsVariants()); + + return $defaultNarrowingHelper->specifyTypesFromConditionalReturnType($context, $isObjectCall, $acceptor, $s); + }; + + $leftTruthyTypes = $leftTypes($scope, TypeSpecifierContext::createTruthy()); + $leftFalseyTypes = $leftTypes($scope, TypeSpecifierContext::createFalsey()); + if ($leftTruthyTypes === null || $leftFalseyTypes === null) { + return null; + } + $leftTruthyScope = $scope->applySpecifiedTypes($leftTruthyTypes->setRootExpr($identical)); + $leftFalseyScope = $scope->applySpecifiedTypes($leftFalseyTypes->setRootExpr($identical)); + $rightTruthyTypes = $rightTypes($leftFalseyScope, TypeSpecifierContext::createTruthy()); + if ($rightTruthyTypes === null) { + return null; + } + $rightTruthyScope = $leftFalseyScope->applySpecifiedTypes($rightTruthyTypes->setRootExpr($isObjectCall)); + + $types = $this->booleanNarrowingHelper->specifyDisjunction( + $nodeScopeResolver, + $scope, + TypeSpecifierContext::createTruthy(), + $condition, + $identical, + static fn (MutatingScope $s, TypeSpecifierContext $context): SpecifiedTypes => $leftTypes($s, $context) ?? new SpecifiedTypes(), + static fn (bool $nativeTypesPromoted): Type => self::emptyArrayComparison($nativeTypesPromoted ? $iterateeResult->getNativeType() : $iterateeResult->getType(), $emptyArrayType), + static fn (): MutatingScope => $leftTruthyScope, + static fn (): MutatingScope => $leftFalseyScope, + $isObjectCall, + static fn (MutatingScope $s, TypeSpecifierContext $context): SpecifiedTypes => $rightTypes($s, $context) ?? new SpecifiedTypes(), + static fn (bool $nativeTypesPromoted): Type => self::isObjectVerdict($nativeTypesPromoted ? $iterateeResult->getNativeType() : $iterateeResult->getType()), + static fn (): MutatingScope => $rightTruthyScope, + ); + + return $scope->applySpecifiedTypes($types); + } + + /** The type of `$iteratee === []`. */ + private static function emptyArrayComparison(Type $iterateeType, ConstantArrayType $emptyArrayType): Type + { + if ($iterateeType->equals($emptyArrayType)) { + return new ConstantBooleanType(true); + } + if ($emptyArrayType->isSuperTypeOf($iterateeType)->no()) { + return new ConstantBooleanType(false); + } + + return new BooleanType(); + } + + /** The type of `is_object($iteratee)`. */ + private static function isObjectVerdict(Type $iterateeType): Type + { + $isObject = $iterateeType->isObject(); + if ($isObject->yes()) { + return new ConstantBooleanType(true); + } + if ($isObject->no()) { + return new ConstantBooleanType(false); + } + + return new BooleanType(); + } + +} diff --git a/src/Analyser/StmtHandler/ForeachHandler.php b/src/Analyser/StmtHandler/ForeachHandler.php index afdc558537c..9bd2d818f92 100644 --- a/src/Analyser/StmtHandler/ForeachHandler.php +++ b/src/Analyser/StmtHandler/ForeachHandler.php @@ -4,13 +4,11 @@ use IteratorAggregate; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp; -use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; @@ -24,6 +22,7 @@ use PHPStan\Analyser\ExpressionResultStorage; use PHPStan\Analyser\ExpressionTypeHolder; use PHPStan\Analyser\ExprHandler\AssignHandler; +use PHPStan\Analyser\ExprHandler\Helper\ForeachEmptyOrObjectNarrowingHelper; use PHPStan\Analyser\ExprHandler\Helper\IdenticalNarrowingHelper; use PHPStan\Analyser\InternalStatementResult; use PHPStan\Analyser\InternalThrowPoint; @@ -473,15 +472,9 @@ static function () use ($condResult, $emptyArrayType): Type { $isIterableAtLeastOnce = $exprType->isIterableAtLeastOnce(); if ($isIterableAtLeastOnce->maybe() || $exprType->isIterable()->no()) { - $finalScope = $finalScope->mergeWith($nodeScopeResolver->narrowScopeWithCondition($scope, new BooleanOr( - new BinaryOp\Identical( - $stmt->expr, - new Array_([]), - ), - new FuncCall(new Name\FullyQualified('is_object'), [ - new Arg($stmt->expr), - ]), - ), TypeSpecifierContext::createTruthy())); + $finalScope = $finalScope->mergeWith( + $this->container->getByType(ForeachEmptyOrObjectNarrowingHelper::class)->narrowToEmptyOrObject($nodeScopeResolver, $scope, $stmt->expr, $condResult, $storage), + ); } elseif ($isIterableAtLeastOnce->no() || $finalScopeResult->isAlwaysTerminating()) { $finalScope = $scope; } elseif (!$this->polluteScopeWithAlwaysIterableForeach) { diff --git a/turbo-ext/src/ForeachHandler.cpp b/turbo-ext/src/ForeachHandler.cpp index 75769cda403..d98a92c834a 100644 --- a/turbo-ext/src/ForeachHandler.cpp +++ b/turbo-ext/src/ForeachHandler.cpp @@ -66,10 +66,10 @@ pt_method_site pt_feh_get_doc_comment_site; /* the permanent interned strings of the twin's literals (module startup) */ zend_string *pt_feh_identical_narrowing_helper_class = nullptr; +zend_string *pt_feh_empty_or_object_narrowing_helper_class = nullptr; zend_string *pt_feh_traversable_class = nullptr; zend_string *pt_feh_iterator_aggregate_class = nullptr; zend_string *pt_feh_get_iterator = nullptr; -zend_string *pt_feh_is_object = nullptr; /* $node->getAttributes() / ->setAttributes($attributes) */ zv::Val getAttributes(zval *node) @@ -765,7 +765,7 @@ class ForeachHandler bool resultAlwaysTerminating; if (UNEXPECTED(!pt_internal_statement_result_is_always_terminating(result, resultAlwaysTerminating))) return zv::Val(); if (narrowToEmpty) { - zv::Val emptyScope = narrowToEmptyOrObject(nodeScopeResolver, scope.raw(), iteratee); + zv::Val emptyScope = narrowToEmptyOrObject(nodeScopeResolver, scope.raw(), iteratee, condResult.raw(), originalStorage); if (UNEXPECTED(emptyScope.isUndef())) return zv::Val(); zv::Val merged = pt_mutating_scope_merge_with(Z_OBJ_P(finalScope.raw()), emptyScope.raw()); if (UNEXPECTED(merged.isUndef())) return zv::Val(); @@ -1224,37 +1224,14 @@ class ForeachHandler return true; } - /* $nodeScopeResolver->narrowScopeWithCondition($scope, new BooleanOr(new - * Identical($iteratee, new Array_([])), new FuncCall(new - * Name\FullyQualified('is_object'), [new Arg($iteratee)])), - * TypeSpecifierContext::createTruthy()) — the nodes created in the - * twin's order */ - static zv::Val narrowToEmptyOrObject(zval *nodeScopeResolver, zval *scope, zval *iteratee) + /* $this->container->getByType(ForeachEmptyOrObjectNarrowingHelper::class) + * ->narrowToEmptyOrObject($nodeScopeResolver, $scope, $stmt->expr, $condResult, $storage) */ + zv::Val narrowToEmptyOrObject(zval *nodeScopeResolver, zval *scope, zval *iteratee, zval *iterateeResult, zval *storage) const { - zv::Val emptyArray = newEmptyArrayExpr(); - if (UNEXPECTED(emptyArray.isUndef())) return zv::Val(); - zv::Args identicalArgv{iteratee, emptyArray.raw()}; - zv::Val identical = pt_type_new(PT_CLASS_IDENTICAL_EXPR, 2, identicalArgv); - if (UNEXPECTED(identical.isUndef())) return zv::Val(); - zval isObject; - ZVAL_INTERNED_STR(&isObject, pt_feh_is_object); - zv::Val name = pt_name_node_new(PT_CLASS_FULLY_QUALIFIED, &isObject); - if (UNEXPECTED(name.isUndef())) return zv::Val(); - zv::Val arg = pt_type_new(PT_CLASS_ARG, 1, iteratee); - if (UNEXPECTED(arg.isUndef())) return zv::Val(); - zv::Arr args = zv::Arr::create(1); - args.push(std::move(arg)); - zv::Args callArgv{name.raw(), args.raw()}; - zv::Val call = pt_type_new(PT_CLASS_FUNC_CALL, 2, callArgv); - if (UNEXPECTED(call.isUndef())) return zv::Val(); - zv::Args orArgv{identical.raw(), call.raw()}; - zv::Val booleanOr = pt_type_new(PT_CLASS_BOOLEAN_OR_EXPR, 2, orArgv); - if (UNEXPECTED(booleanOr.isUndef())) return zv::Val(); - zend_object *truthy = pt_type_specifier_context_create_truthy(); - if (UNEXPECTED(truthy == NULL)) return zv::Val(); - zval truthyContext; - ZVAL_OBJ(&truthyContext, truthy); - return pt_node_scope_resolver_narrow_scope_with_condition(nodeScopeResolver, scope, booleanOr.raw(), &truthyContext); + zv::Val helper = ptlh::containerGetByType(OBJ_PROP_NUM(self, slots::container), pt_feh_empty_or_object_narrowing_helper_class); + if (UNEXPECTED(helper.isUndef())) return zv::Val(); + zv::Args argv{nodeScopeResolver, scope, iteratee, iterateeResult, storage}; + return pt_type_call(Z_OBJ_P(helper.raw()), PT_LC("narrowtoemptyorobject"), 5, argv); } /* Mirrors the private enterForeach(). */ @@ -2012,10 +1989,10 @@ using phpstanturbo::ForeachHandler; PT_MINIT_REGISTRATION(pt_register_foreach_handler) { pt_feh_identical_narrowing_helper_class = zend_string_init_interned(PT_LC("PHPStan\\Analyser\\ExprHandler\\Helper\\IdenticalNarrowingHelper"), 1); + pt_feh_empty_or_object_narrowing_helper_class = zend_string_init_interned(PT_LC("PHPStan\\Analyser\\ExprHandler\\Helper\\ForeachEmptyOrObjectNarrowingHelper"), 1); pt_feh_traversable_class = zend_string_init_interned(PT_LC("Traversable"), 1); pt_feh_iterator_aggregate_class = zend_string_init_interned(PT_LC("IteratorAggregate"), 1); pt_feh_get_iterator = zend_string_init_interned(PT_LC("getIterator"), 1); - pt_feh_is_object = zend_string_init_interned(PT_LC("is_object"), 1); reg::Class cls("PHPStan\\Analyser\\StmtHandler\\ForeachHandler"); ptdecl::ForeachHandler::declareClass(cls); From 5f9c0e008190b0419a06928a6202046e2414b64f Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 08:55:11 +0200 Subject: [PATCH 6/8] Bump expected turbo version --- src/Turbo/TurboExtensionEnabler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index 22991812fa2..ea6c30b868b 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -33,7 +33,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = '1cb5faf'; + public const EXPECTED_EXTENSION_VERSION = 'eed1872'; private static bool $active = false; From 80f2bb1ce074ec532e35be08d8b622c804b3f443 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 09:19:23 +0200 Subject: [PATCH 7/8] Shadow ForeachEmptyOrObjectNarrowingHelper natively The helper ForeachHandler calls after a loop that may not iterate gets its native twin: narrowToEmptyOrObject() builds the disjunction and composes its narrowing through the direct entries of IdenticalNarrowingHelper, DefaultNarrowingHelper, ParametersAcceptor selection and BooleanNarrowingHelper, with the twin's closures as native closures over the same captures. Covered by walk-trace. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MMNnG2Zo7wgvSZCzjdk17X --- .../ForeachEmptyOrObjectNarrowingHelper.php | 2 + .../ForeachEmptyOrObjectNarrowingHelper.cpp | 426 ++++++++++++++++++ .../ForeachEmptyOrObjectNarrowingHelper.h | 109 +++++ turbo-ext/tests/smoke.php | 1 + 4 files changed, 538 insertions(+) create mode 100644 turbo-ext/src/ForeachEmptyOrObjectNarrowingHelper.cpp create mode 100644 turbo-ext/src/generated/ForeachEmptyOrObjectNarrowingHelper.h diff --git a/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php b/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php index 63decb2ed4e..31aeb817482 100644 --- a/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php +++ b/src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php @@ -18,6 +18,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Turbo\ShadowedByTurboExtension; use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; @@ -28,6 +29,7 @@ * to what an unentered loop leaves: `$iteratee === [] || is_object($iteratee)`. */ #[AutowiredService] +#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../../../turbo-ext/src/ForeachEmptyOrObjectNarrowingHelper.cpp')] final class ForeachEmptyOrObjectNarrowingHelper { diff --git a/turbo-ext/src/ForeachEmptyOrObjectNarrowingHelper.cpp b/turbo-ext/src/ForeachEmptyOrObjectNarrowingHelper.cpp new file mode 100644 index 00000000000..90b79dc1c38 --- /dev/null +++ b/turbo-ext/src/ForeachEmptyOrObjectNarrowingHelper.cpp @@ -0,0 +1,426 @@ +/* + * PHPStanTurbo\ForeachEmptyOrObjectNarrowingHelper — native implementation of + * PHPStan\Analyser\ExprHandler\Helper\ForeachEmptyOrObjectNarrowingHelper. + * + * A DI service (#[AutowiredService]): the constructor keeps the twin's + * arginfo so Nette autowires it, the four collaborators live in the twin's + * property slots. narrowToEmptyOrObject() composes the truthy narrowing of + * `$iteratee === [] || is_object($iteratee)` through + * BooleanNarrowingHelper::specifyDisjunction() the way the twin does; the + * twin's closures are native closures over the same captures, and the + * collaborators are called through their direct entries. + */ + +#include "support.h" +#include "generated/ForeachEmptyOrObjectNarrowingHelper.h" + +namespace slots = ptdecl::ForeachEmptyOrObjectNarrowingHelper::slot; +namespace sigs = ptdecl::ForeachEmptyOrObjectNarrowingHelper::sig; +#include "zv.h" +#include "TypeTraits.h" +#include "TypeOps.h" +#include "Engine.h" +#include "AnalyserValues.h" + +zend_class_entry *pt_ce_foreach_empty_or_object_narrowing_helper = nullptr; + +/* the permanent interned string of the twin's literal (module startup) */ +static zend_string *pt_feoh_is_object = nullptr; + +namespace { + +/* new FullyQualified('is_object') */ +zv::Val newIsObjectName() +{ + zval name; + ZVAL_INTERNED_STR(&name, pt_feoh_is_object); + return pt_name_node_new(PT_CLASS_FULLY_QUALIFIED, &name); +} + +/* TypeSpecifierContext::createTruthy() / ::createFalsey() as a zval */ +bool contextZval(zval *out, bool truthy) +{ + zend_object *context = truthy ? pt_type_specifier_context_create_truthy() : pt_type_specifier_context_create_falsey(); + if (UNEXPECTED(context == NULL)) return false; + ZVAL_OBJ(out, context); + return true; +} + +/* self::emptyArrayComparison(): the type of `$iteratee === []` */ +zv::Val emptyArrayComparison(zval *iterateeType, zval *emptyArrayType) +{ + if (UNEXPECTED(Z_TYPE_P(iterateeType) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function equals() on %s", zend_zval_value_name(iterateeType)); + return zv::Val(); + } + zv::Val equals = pt_type_op(Z_OBJ_P(iterateeType), PT_OP_EQUALS, 1, emptyArrayType); + if (UNEXPECTED(equals.isUndef())) return zv::Val(); + zval out; + if (zend_is_true(equals.raw())) { + if (UNEXPECTED(!pt_constant_boolean_type_new(&out, true))) return zv::Val(); + return zv::Val::adopt(out); + } + zv::Val superType = pt_type_op(Z_OBJ_P(emptyArrayType), PT_OP_IS_SUPER_TYPE_OF, 1, iterateeType); + if (UNEXPECTED(superType.isUndef())) return zv::Val(); + if (UNEXPECTED(Z_TYPE_P(superType.raw()) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function no() on %s", zend_zval_value_name(superType.raw())); + return zv::Val(); + } + if (pt_result_value(Z_OBJ_P(superType.raw())) == PT_TRI_NO) { + if (UNEXPECTED(!pt_constant_boolean_type_new(&out, false))) return zv::Val(); + return zv::Val::adopt(out); + } + if (UNEXPECTED(!pt_boolean_type_new(&out))) return zv::Val(); + return zv::Val::adopt(out); +} + +/* self::isObjectVerdict(): the type of `is_object($iteratee)` */ +zv::Val isObjectVerdict(zval *iterateeType) +{ + if (UNEXPECTED(Z_TYPE_P(iterateeType) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function isObject() on %s", zend_zval_value_name(iterateeType)); + return zv::Val(); + } + zend_long isObject = pt_type_call_trinary(Z_OBJ_P(iterateeType), PT_LC("isobject"), 0, NULL); + if (UNEXPECTED(isObject < 0)) return zv::Val(); + zval out; + if (isObject == PT_TRI_YES) { + if (UNEXPECTED(!pt_constant_boolean_type_new(&out, true))) return zv::Val(); + } else if (isObject == PT_TRI_NO) { + if (UNEXPECTED(!pt_constant_boolean_type_new(&out, false))) return zv::Val(); + } else if (UNEXPECTED(!pt_boolean_type_new(&out))) { + return zv::Val(); + } + return zv::Val::adopt(out); +} + +/* $iterateeResult->getType() / ->getNativeType() */ +zv::Val iterateeTypeOf(zval *iterateeResult, bool nativeTypesPromoted) +{ + return nativeTypesPromoted ? pt_expression_result_get_native_type(iterateeResult) : pt_expression_result_get_type(iterateeResult); +} + +/* static fn (): Type => self::emptyArrayComparison($iterateeResult->getType(), $emptyArrayType) + * — captures: $iterateeResult, $emptyArrayType */ +void identicalTypeBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + (void) argc; + (void) argv; + zv::Val iterateeType = pt_expression_result_get_type(&captures[0]); + if (UNEXPECTED(iterateeType.isUndef())) return; + zv::Val type = emptyArrayComparison(iterateeType.raw(), &captures[1]); + if (UNEXPECTED(type.isUndef())) return; + type.intoReturnValue(return_value); +} + +/* $leftTypes($s, $context): $identicalNarrowingHelper->specifyIdenticalAgainstType(...) + * — captures: $identicalNarrowingHelper, $identical->left, $iterateeResult, + * $identical->right, $emptyArrayType, $subjectArgResult, the identical type + * callback; null when the comparison cannot be composed */ +zv::Val leftTypesOf(zval *captures, zval *s, zval *context) +{ + return pt_identical_narrowing_helper_specify_identical_against_type(&captures[0], &captures[1], &captures[2], &captures[3], &captures[4], context, s, &captures[5], &captures[6]); +} + +/* static fn (MutatingScope $s, TypeSpecifierContext $context): SpecifiedTypes + * => $leftTypes($s, $context) ?? new SpecifiedTypes() */ +void leftTypesBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + if (UNEXPECTED(argc < 2)) { + zend_throw_error(NULL, "phpstan_turbo: the left narrowing callback expects a scope and a context"); + return; + } + zv::Val types = leftTypesOf(captures, &argv[0], &argv[1]); + if (UNEXPECTED(types.isUndef())) return; + if (types.isNull()) types = pt_specified_types_new(); + if (UNEXPECTED(types.isUndef())) return; + types.intoReturnValue(return_value); +} + +/* $rightTypes($s, $context): the conditional return type narrowing of + * is_object() — captures: $defaultNarrowingHelper, $isObjectCall, its args, + * $isObjectFunction; null when there is none */ +zv::Val rightTypesOf(zval *captures, zval *s, zval *context) +{ + zval *isObjectFunction = &captures[3]; + if (UNEXPECTED(Z_TYPE_P(isObjectFunction) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function getVariants() on %s", zend_zval_value_name(isObjectFunction)); + return zv::Val(); + } + zv::Val variants = pt_type_call(Z_OBJ_P(isObjectFunction), PT_LC("getvariants"), 0, NULL); + if (UNEXPECTED(variants.isUndef())) return zv::Val(); + zv::Val namedArgumentsVariants = pt_type_call(Z_OBJ_P(isObjectFunction), PT_LC("getnamedargumentsvariants"), 0, NULL); + if (UNEXPECTED(namedArgumentsVariants.isUndef())) return zv::Val(); + zv::Val acceptor = pt_parameters_acceptor_selector_select_from_args(s, &captures[2], variants.raw(), namedArgumentsVariants.raw()); + if (UNEXPECTED(acceptor.isUndef())) return zv::Val(); + return pt_default_narrowing_helper_specify_types_from_conditional_return_type(&captures[0], context, &captures[1], acceptor.raw(), s); +} + +/* static fn (MutatingScope $s, TypeSpecifierContext $context): SpecifiedTypes + * => $rightTypes($s, $context) ?? new SpecifiedTypes() */ +void rightTypesBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + if (UNEXPECTED(argc < 2)) { + zend_throw_error(NULL, "phpstan_turbo: the right narrowing callback expects a scope and a context"); + return; + } + zv::Val types = rightTypesOf(captures, &argv[0], &argv[1]); + if (UNEXPECTED(types.isUndef())) return; + if (types.isNull()) types = pt_specified_types_new(); + if (UNEXPECTED(types.isUndef())) return; + types.intoReturnValue(return_value); +} + +/* static fn (bool $nativeTypesPromoted): Type => self::emptyArrayComparison(...) + * — captures: $iterateeResult, $emptyArrayType */ +void leftTypeBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + zv::Val iterateeType = iterateeTypeOf(&captures[0], argc > 0 && zend_is_true(&argv[0])); + if (UNEXPECTED(iterateeType.isUndef())) return; + zv::Val type = emptyArrayComparison(iterateeType.raw(), &captures[1]); + if (UNEXPECTED(type.isUndef())) return; + type.intoReturnValue(return_value); +} + +/* static fn (bool $nativeTypesPromoted): Type => self::isObjectVerdict(...) + * — captures: $iterateeResult */ +void rightTypeBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + zv::Val iterateeType = iterateeTypeOf(&captures[0], argc > 0 && zend_is_true(&argv[0])); + if (UNEXPECTED(iterateeType.isUndef())) return; + zv::Val type = isObjectVerdict(iterateeType.raw()); + if (UNEXPECTED(type.isUndef())) return; + type.intoReturnValue(return_value); +} + +/* static fn (): MutatingScope => $scope — captures: $scope */ +void scopeBody(zval *captures, uint32_t argc, zval *argv, zval *return_value) +{ + (void) argc; + (void) argv; + ZVAL_COPY(return_value, &captures[0]); +} + +/* $types->setRootExpr($rootExpr) applied to $scope */ +zv::Val applyRooted(zval *scope, zval *types, zval *rootExpr) +{ + zv::Val rooted = pt_specified_types_set_root_expr(Z_OBJ_P(types), rootExpr); + if (UNEXPECTED(rooted.isUndef())) return zv::Val(); + return pt_mutating_scope_apply_specified_types(Z_OBJ_P(scope), rooted.raw()); +} + +} // namespace + +namespace phpstanturbo { + +/* Mirrors PHPStan\Analyser\ExprHandler\Helper\ForeachEmptyOrObjectNarrowingHelper. */ +class ForeachEmptyOrObjectNarrowingHelper +{ +public: + explicit ForeachEmptyOrObjectNarrowingHelper(zend_object *self) : self(self) {} + + /* __construct(private ReflectionProvider $reflectionProvider, private + * IdenticalNarrowingHelper $identicalNarrowingHelper, private + * DefaultNarrowingHelper $defaultNarrowingHelper, private + * BooleanNarrowingHelper $booleanNarrowingHelper) */ + void construct(zval *reflectionProvider, zval *identicalNarrowingHelper, zval *defaultNarrowingHelper, zval *booleanNarrowingHelper) const + { + pt_write_slot(self, slots::reflectionProvider, reflectionProvider); + pt_write_slot(self, slots::identicalNarrowingHelper, identicalNarrowingHelper); + pt_write_slot(self, slots::defaultNarrowingHelper, defaultNarrowingHelper); + pt_write_slot(self, slots::booleanNarrowingHelper, booleanNarrowingHelper); + } + + /* Mirrors narrowToEmptyOrObject(). UNDEF = pending exception */ + zv::Val narrowToEmptyOrObject(zval *nodeScopeResolver, zval *scope, zval *iteratee, zval *iterateeResult, zval *storage) const + { + zval emptyItems; + ZVAL_EMPTY_ARRAY(&emptyItems); + zv::Val emptyArray = pt_type_new(PT_CLASS_ARRAY_EXPR, 1, &emptyItems); + if (UNEXPECTED(emptyArray.isUndef())) return zv::Val(); + zv::Args identicalArgv{iteratee, emptyArray.raw()}; + zv::Val identical = pt_type_new(PT_CLASS_IDENTICAL_EXPR, 2, identicalArgv); + if (UNEXPECTED(identical.isUndef())) return zv::Val(); + zv::Val name = newIsObjectName(); + if (UNEXPECTED(name.isUndef())) return zv::Val(); + zv::Val arg = pt_type_new(PT_CLASS_ARG, 1, iteratee); + if (UNEXPECTED(arg.isUndef())) return zv::Val(); + zv::Arr args = zv::Arr::create(1); + args.push(std::move(arg)); + zv::Args callArgv{name.raw(), args.raw()}; + zv::Val isObjectCall = pt_type_new(PT_CLASS_FUNC_CALL, 2, callArgv); + if (UNEXPECTED(isObjectCall.isUndef())) return zv::Val(); + zv::Args orArgv{identical.raw(), isObjectCall.raw()}; + zv::Val condition = pt_type_new(PT_CLASS_BOOLEAN_OR_EXPR, 2, orArgv); + if (UNEXPECTED(condition.isUndef())) return zv::Val(); + + zv::Val composed = composeScope(nodeScopeResolver, scope, condition.raw(), identical.raw(), iteratee, emptyArray.raw(), isObjectCall.raw(), args.raw(), iterateeResult, storage); + if (UNEXPECTED(composed.isUndef())) return zv::Val(); + if (!composed.isNull()) return composed; + zval truthy; + if (UNEXPECTED(!contextZval(&truthy, true))) return zv::Val(); + return pt_node_scope_resolver_narrow_scope_with_condition(nodeScopeResolver, scope, condition.raw(), &truthy); + } + + /* Mirrors the private composeScope(); null when a side cannot be + * composed, UNDEF = pending exception. The nodes' operands come in + * directly ($identical->left / ->right, the call's args) instead of + * being read back */ + zv::Val composeScope(zval *nodeScopeResolver, zval *scope, zval *condition, zval *identical, zval *identicalLeft, zval *identicalRight, zval *isObjectCall, zval *isObjectArgs, zval *iterateeResult, zval *storage) const + { + zval *reflectionProvider = OBJ_PROP_NUM(self, slots::reflectionProvider); + zval *identicalNarrowingHelper = OBJ_PROP_NUM(self, slots::identicalNarrowingHelper); + zval *defaultNarrowingHelper = OBJ_PROP_NUM(self, slots::defaultNarrowingHelper); + zval *booleanNarrowingHelper = OBJ_PROP_NUM(self, slots::booleanNarrowingHelper); + if (UNEXPECTED(Z_TYPE_P(booleanNarrowingHelper) != IS_OBJECT)) { + zend_throw_error(NULL, "Call to a member function specifyDisjunction() on %s", zend_zval_value_name(booleanNarrowingHelper)); + return zv::Val(); + } + + zv::Val isObjectName = newIsObjectName(); + if (UNEXPECTED(isObjectName.isUndef())) return zv::Val(); + bool hasFunction; + if (UNEXPECTED(!pt_reflection_provider_has_function(reflectionProvider, isObjectName.raw(), scope, hasFunction))) return zv::Val(); + if (!hasFunction) return zv::Val::null(); + zv::Val isObjectFunction = pt_reflection_provider_get_function(reflectionProvider, isObjectName.raw(), scope); + if (UNEXPECTED(isObjectFunction.isUndef())) return zv::Val(); + zv::Val emptyArrayType; + { + zval keyTypes, valueTypes, out; + ZVAL_EMPTY_ARRAY(&keyTypes); + ZVAL_EMPTY_ARRAY(&valueTypes); + if (UNEXPECTED(!pt_constant_array_type_new(&out, &keyTypes, &valueTypes))) return zv::Val(); + emptyArrayType = zv::Val::adopt(out); + } + zv::Val subjectArgResult = pt_identical_narrowing_helper_capture_first_arg_result(identicalNarrowingHelper, identicalLeft, storage); + if (UNEXPECTED(subjectArgResult.isUndef())) return zv::Val(); + + zv::Val identicalTypeCallback = pt_native_closure(&identicalTypeBody, iterateeResult, emptyArrayType.raw()); + zval leftCaptures[7]; + ZVAL_COPY_VALUE(&leftCaptures[0], identicalNarrowingHelper); + ZVAL_COPY_VALUE(&leftCaptures[1], identicalLeft); + ZVAL_COPY_VALUE(&leftCaptures[2], iterateeResult); + ZVAL_COPY_VALUE(&leftCaptures[3], identicalRight); + ZVAL_COPY_VALUE(&leftCaptures[4], emptyArrayType.raw()); + ZVAL_COPY_VALUE(&leftCaptures[5], subjectArgResult.raw()); + ZVAL_COPY_VALUE(&leftCaptures[6], identicalTypeCallback.raw()); + zval rightCaptures[4]; + ZVAL_COPY_VALUE(&rightCaptures[0], defaultNarrowingHelper); + ZVAL_COPY_VALUE(&rightCaptures[1], isObjectCall); + ZVAL_COPY_VALUE(&rightCaptures[2], isObjectArgs); + ZVAL_COPY_VALUE(&rightCaptures[3], isObjectFunction.raw()); + + zval truthy, falsey; + if (UNEXPECTED(!contextZval(&truthy, true) || !contextZval(&falsey, false))) return zv::Val(); + zv::Val leftTruthyTypes = leftTypesOf(leftCaptures, scope, &truthy); + if (UNEXPECTED(leftTruthyTypes.isUndef())) return zv::Val(); + zv::Val leftFalseyTypes = leftTypesOf(leftCaptures, scope, &falsey); + if (UNEXPECTED(leftFalseyTypes.isUndef())) return zv::Val(); + if (leftTruthyTypes.isNull() || leftFalseyTypes.isNull()) return zv::Val::null(); + zv::Val leftTruthyScope = applyRooted(scope, leftTruthyTypes.raw(), identical); + if (UNEXPECTED(leftTruthyScope.isUndef())) return zv::Val(); + zv::Val leftFalseyScope = applyRooted(scope, leftFalseyTypes.raw(), identical); + if (UNEXPECTED(leftFalseyScope.isUndef())) return zv::Val(); + zv::Val rightTruthyTypes = rightTypesOf(rightCaptures, leftFalseyScope.raw(), &truthy); + if (UNEXPECTED(rightTruthyTypes.isUndef())) return zv::Val(); + if (rightTruthyTypes.isNull()) return zv::Val::null(); + zv::Val rightTruthyScope = applyRooted(leftFalseyScope.raw(), rightTruthyTypes.raw(), isObjectCall); + if (UNEXPECTED(rightTruthyScope.isUndef())) return zv::Val(); + + zv::Val leftTypesCallback = pt_native_closure_new(&leftTypesBody, 7, leftCaptures); + zv::Val leftTypeCallback = pt_native_closure(&leftTypeBody, iterateeResult, emptyArrayType.raw()); + zv::Val leftTruthyScopeCallback = pt_native_closure(&scopeBody, leftTruthyScope.raw()); + zv::Val leftFalseyScopeCallback = pt_native_closure(&scopeBody, leftFalseyScope.raw()); + zv::Val rightTypesCallback = pt_native_closure_new(&rightTypesBody, 4, rightCaptures); + zv::Val rightTypeCallback = pt_native_closure(&rightTypeBody, iterateeResult); + zv::Val rightTruthyScopeCallback = pt_native_closure(&scopeBody, rightTruthyScope.raw()); + zv::Val types = pt_boolean_narrowing_helper_specify_disjunction( + Z_OBJ_P(booleanNarrowingHelper), + nodeScopeResolver, + scope, + &truthy, + condition, + identical, + leftTypesCallback.raw(), + leftTypeCallback.raw(), + leftTruthyScopeCallback.raw(), + leftFalseyScopeCallback.raw(), + isObjectCall, + rightTypesCallback.raw(), + rightTypeCallback.raw(), + rightTruthyScopeCallback.raw() + ); + if (UNEXPECTED(types.isUndef())) return zv::Val(); + return pt_mutating_scope_apply_specified_types(Z_OBJ_P(scope), types.raw()); + } + +private: + zend_object *self; +}; + +} // namespace phpstanturbo + +using phpstanturbo::ForeachEmptyOrObjectNarrowingHelper; + +/* {{{ engine ABI glue: parameter parsing + registration */ + +#include "reg.h" + +PT_MINIT_REGISTRATION(pt_register_foreach_empty_or_object_narrowing_helper) +{ + pt_feoh_is_object = zend_string_init_interned(PT_LC("is_object"), 1); + + reg::Class cls("PHPStan\\Analyser\\ExprHandler\\Helper\\ForeachEmptyOrObjectNarrowingHelper"); + ptdecl::ForeachEmptyOrObjectNarrowingHelper::declareClass(cls); + ptdecl::ForeachEmptyOrObjectNarrowingHelper::declareProperties(cls); + + /* the real parameter class names: the DI container autowires the + * service by reflecting the constructor */ + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *reflectionProvider, *identicalNarrowingHelper, *defaultNarrowingHelper, *booleanNarrowingHelper; + if (!zp::parse(execute_data, reflectionProvider, identicalNarrowingHelper, defaultNarrowingHelper, booleanNarrowingHelper)) RETURN_THROWS(); + ForeachEmptyOrObjectNarrowingHelper(Z_OBJ_P(ZEND_THIS)).construct(reflectionProvider, identicalNarrowingHelper, defaultNarrowingHelper, booleanNarrowingHelper); + }); + + cls.method(sigs::narrowToEmptyOrObject, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *nodeScopeResolver, *scope, *iteratee, *iterateeResult, *storage; + if (!zp::parse(execute_data, nodeScopeResolver, scope, iteratee, iterateeResult, storage)) RETURN_THROWS(); + PT_RETURN_VAL(ForeachEmptyOrObjectNarrowingHelper(Z_OBJ_P(ZEND_THIS)).narrowToEmptyOrObject(nodeScopeResolver, scope, iteratee, iterateeResult, storage)); + }); + + cls.method(sigs::composeScope, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *nodeScopeResolver, *scope, *condition, *identical, *isObjectCall, *iterateeResult, *storage; + ZEND_PARSE_PARAMETERS_START(7, 7) + Z_PARAM_OBJECT(nodeScopeResolver) + Z_PARAM_OBJECT(scope) + Z_PARAM_OBJECT(condition) + Z_PARAM_OBJECT(identical) + Z_PARAM_OBJECT(isObjectCall) + Z_PARAM_OBJECT(iterateeResult) + Z_PARAM_OBJECT(storage) + ZEND_PARSE_PARAMETERS_END(); + /* the private method's operands read back from the nodes, as the twin does */ + zval leftRv, rightRv, argsRv; + zval *left = zend_read_property(Z_OBJCE_P(identical), Z_OBJ_P(identical), PT_LC("left"), 0, &leftRv); + zval *right = zend_read_property(Z_OBJCE_P(identical), Z_OBJ_P(identical), PT_LC("right"), 0, &rightRv); + zval *args = zend_read_property(Z_OBJCE_P(isObjectCall), Z_OBJ_P(isObjectCall), PT_LC("args"), 0, &argsRv); + if (UNEXPECTED(EG(exception))) RETURN_THROWS(); + PT_RETURN_VAL(ForeachEmptyOrObjectNarrowingHelper(Z_OBJ_P(ZEND_THIS)).composeScope(nodeScopeResolver, scope, condition, identical, left, right, isObjectCall, args, iterateeResult, storage)); + }); + + cls.method(sigs::emptyArrayComparison, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *iterateeType, *emptyArrayType; + if (!zp::parse(execute_data, iterateeType, emptyArrayType)) RETURN_THROWS(); + PT_RETURN_VAL(emptyArrayComparison(iterateeType, emptyArrayType)); + }); + + cls.method(sigs::isObjectVerdict, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *iterateeType; + if (!zp::parse(execute_data, iterateeType)) RETURN_THROWS(); + PT_RETURN_VAL(isObjectVerdict(iterateeType)); + }); + + cls.shadow(&pt_ce_foreach_empty_or_object_narrowing_helper); +} + +/* }}} */ diff --git a/turbo-ext/src/generated/ForeachEmptyOrObjectNarrowingHelper.h b/turbo-ext/src/generated/ForeachEmptyOrObjectNarrowingHelper.h new file mode 100644 index 00000000000..4a9725736e2 --- /dev/null +++ b/turbo-ext/src/generated/ForeachEmptyOrObjectNarrowingHelper.h @@ -0,0 +1,109 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Analyser/ExprHandler/Helper/ForeachEmptyOrObjectNarrowingHelper.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_FOREACH_EMPTY_OR_OBJECT_NARROWING_HELPER_H +#define PHPSTANTURBO_GENERATED_FOREACH_EMPTY_OR_OBJECT_NARROWING_HELPER_H + +#include "../reg.h" + +namespace ptdecl::ForeachEmptyOrObjectNarrowingHelper { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t reflectionProvider = 0; +inline constexpr uint32_t identicalNarrowingHelper = 1; +inline constexpr uint32_t defaultNarrowingHelper = 2; +inline constexpr uint32_t booleanNarrowingHelper = 3; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("reflectionProvider", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Reflection\\ReflectionProvider"); + cls.property("identicalNarrowingHelper", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExprHandler\\Helper\\IdenticalNarrowingHelper"); + cls.property("defaultNarrowingHelper", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExprHandler\\Helper\\DefaultNarrowingHelper"); + cls.property("booleanNarrowingHelper", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExprHandler\\Helper\\BooleanNarrowingHelper"); +} + +/* the string and parameter tables the signatures below index into (see reg::Sig) */ +namespace sigtab { +inline constexpr char strings[] = + "reflectionProvider\0" /* 0 */ + "PHPStan\\Reflection\\ReflectionProvider\0" /* 19 */ + "identicalNarrowingHelper\0" /* 57 */ + "PHPStan\\Analyser\\ExprHandler\\Helper\\IdenticalNarrowingHelper\0" /* 82 */ + "defaultNarrowingHelper\0" /* 143 */ + "PHPStan\\Analyser\\ExprHandler\\Helper\\DefaultNarrowingHelper\0" /* 166 */ + "booleanNarrowingHelper\0" /* 225 */ + "PHPStan\\Analyser\\ExprHandler\\Helper\\BooleanNarrowingHelper\0" /* 248 */ + "__construct\0" /* 307 */ + "nodeScopeResolver\0" /* 319 */ + "PHPStan\\Analyser\\NodeScopeResolver\0" /* 337 */ + "scope\0" /* 372 */ + "PHPStan\\Analyser\\MutatingScope\0" /* 378 */ + "iteratee\0" /* 409 */ + "PhpParser\\Node\\Expr\0" /* 418 */ + "iterateeResult\0" /* 438 */ + "PHPStan\\Analyser\\ExpressionResult\0" /* 453 */ + "storage\0" /* 487 */ + "PHPStan\\Analyser\\ExpressionResultStorage\0" /* 495 */ + "\0" /* 536 */ + "narrowToEmptyOrObject\0" /* 537 */ + "condition\0" /* 559 */ + "PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr\0" /* 569 */ + "identical\0" /* 608 */ + "PhpParser\\Node\\Expr\\BinaryOp\\Identical\0" /* 618 */ + "isObjectCall\0" /* 657 */ + "PhpParser\\Node\\Expr\\FuncCall\0" /* 670 */ + "composeScope\0" /* 699 */ + "iterateeType\0" /* 712 */ + "PHPStan\\Type\\Type\0" /* 725 */ + "emptyArrayType\0" /* 743 */ + "PHPStan\\Type\\Constant\\ConstantArrayType\0" /* 758 */ + "emptyArrayComparison\0" /* 798 */ + "isObjectVerdict"; /* 819 */ +inline constexpr reg::PackedArg args[] = { + reg::packed(0, 0, 19), /* __construct $reflectionProvider */ + reg::packed(57, 0, 82), /* __construct $identicalNarrowingHelper */ + reg::packed(143, 0, 166), /* __construct $defaultNarrowingHelper */ + reg::packed(225, 0, 248), /* __construct $booleanNarrowingHelper */ + reg::packed(319, 0, 337), /* narrowToEmptyOrObject $nodeScopeResolver */ + reg::packed(372, 0, 378), /* narrowToEmptyOrObject $scope */ + reg::packed(409, 0, 418), /* narrowToEmptyOrObject $iteratee */ + reg::packed(438, 0, 453), /* narrowToEmptyOrObject $iterateeResult */ + reg::packed(487, 0, 495), /* narrowToEmptyOrObject $storage */ + reg::packed(536, 0, 378), /* narrowToEmptyOrObject return */ + reg::packed(319, 0, 337), /* composeScope $nodeScopeResolver */ + reg::packed(372, 0, 378), /* composeScope $scope */ + reg::packed(559, 0, 569), /* composeScope $condition */ + reg::packed(608, 0, 618), /* composeScope $identical */ + reg::packed(657, 0, 670), /* composeScope $isObjectCall */ + reg::packed(438, 0, 453), /* composeScope $iterateeResult */ + reg::packed(487, 0, 495), /* composeScope $storage */ + reg::packed(536, MAY_BE_NULL, 378), /* composeScope return */ + reg::packed(712, 0, 725), /* emptyArrayComparison $iterateeType */ + reg::packed(743, 0, 758), /* emptyArrayComparison $emptyArrayType */ + reg::packed(536, 0, 725), /* emptyArrayComparison return */ + reg::packed(712, 0, 725), /* isObjectVerdict $iterateeType */ + reg::packed(536, 0, 725), /* isObjectVerdict return */ +}; +using Sig = reg::Sig; +} // namespace sigtab + +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr sigtab::Sig __construct = { { 307 /* __construct */, 4, 0, 4, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig narrowToEmptyOrObject = { { 537 /* narrowToEmptyOrObject */, 5, 4, 5, 9, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig composeScope = { { 699 /* composeScope */, 7, 10, 7, 17, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig emptyArrayComparison = { { 798 /* emptyArrayComparison */, 2, 18, 2, 20, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig isObjectVerdict = { { 819 /* isObjectVerdict */, 1, 21, 1, 22, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +} // namespace sig + +} // namespace ptdecl::ForeachEmptyOrObjectNarrowingHelper + +#endif diff --git a/turbo-ext/tests/smoke.php b/turbo-ext/tests/smoke.php index 9df729e25f5..aa666f9f629 100644 --- a/turbo-ext/tests/smoke.php +++ b/turbo-ext/tests/smoke.php @@ -4719,6 +4719,7 @@ public function getComments(): array \PHPStan\Analyser\ExprHandler\BinaryOpHandler::class => 'walk-trace.php', \PHPStan\Analyser\ExprHandler\CoalesceHandler::class => 'walk-trace.php', \PHPStan\Analyser\ExprHandler\Helper\CoalesceCompositionHelper::class => 'walk-trace.php', + \PHPStan\Analyser\ExprHandler\Helper\ForeachEmptyOrObjectNarrowingHelper::class => 'walk-trace.php', \PHPStan\Analyser\StmtHandler\BreakContinueHandler::class => 'walk-trace.php', \PHPStan\Analyser\StmtHandler\LabelHandler::class => 'walk-trace.php', \PHPStan\Analyser\StmtHandler\GotoHandler::class => 'walk-trace.php', From 97902f57bec5ebd4672eac34db80a97806f950b5 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Oct 2026 09:19:23 +0200 Subject: [PATCH 8/8] Bump expected turbo version --- src/Turbo/TurboExtensionEnabler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index ea6c30b868b..4f75bb12110 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -33,7 +33,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = 'eed1872'; + public const EXPECTED_EXTENSION_VERSION = '80f2bb1'; private static bool $active = false;