From bef3992a9bed8fe1c5539523976733add9f52190 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Sun, 26 Jul 2026 10:34:06 +0900 Subject: [PATCH] Narrow foreach body to non-empty behind an opt-in toggle Inside a foreach body the iterated expression is provably non-empty, so it can be narrowed (list to non-empty-list, array to non-empty-array) at body entry. Before, this happened only when polluteScopeWithAlwaysIterableForeach was on, so with the flag off (as phpstan-strict-rules sets it) the narrowing disappeared even though the body is only entered when the iteratee is non-empty. The narrowing is gated behind the new feature toggle narrowForeachBodyNonEmpty, off by default. With it off the body scope is built exactly as before, so no behaviour changes. With it on, the body narrows regardless of the flag. The after-loop scope keeps that precision (a value variable can be seen as defined where the iteratee is later proven non-empty), which is why the toggle is opt-in and not enabled under bleedingEdge yet. Closes phpstan/phpstan#13312 Amp-Thread-ID: https://ampcode.com/threads/T-01a19219-6260-7460-a52b-a418f709e4bf --- conf/config.neon | 1 + conf/parametersSchema.neon | 1 + src/Analyser/StmtHandler/ForeachHandler.php | 9 +- .../Analyser/Bug13312NoPolluteTest.php | 36 +++++ tests/PHPStan/Analyser/Bug13312StableTest.php | 36 +++++ .../Analyser/bug-13312-no-pollute.neon | 4 + tests/PHPStan/Analyser/bug-13312-stable.neon | 2 + .../Analyser/data/bug-13312-no-pollute.php | 37 +++++ .../Analyser/data/bug-13312-stable.php | 32 ++++ tests/PHPStan/Analyser/nsrt/bug-13312.php | 18 +++ turbo-ext/src/ForeachHandler.cpp | 16 +- turbo-ext/src/generated/ForeachHandler.h | 152 +++++++++--------- 12 files changed, 262 insertions(+), 82 deletions(-) create mode 100644 tests/PHPStan/Analyser/Bug13312NoPolluteTest.php create mode 100644 tests/PHPStan/Analyser/Bug13312StableTest.php create mode 100644 tests/PHPStan/Analyser/bug-13312-no-pollute.neon create mode 100644 tests/PHPStan/Analyser/bug-13312-stable.neon create mode 100644 tests/PHPStan/Analyser/data/bug-13312-no-pollute.php create mode 100644 tests/PHPStan/Analyser/data/bug-13312-stable.php diff --git a/conf/config.neon b/conf/config.neon index 0eb6c0c764b..0bfc1acfc18 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -47,6 +47,7 @@ parameters: rawMessageInBaseline: false reportNestedTooWideType: false assignToByRefForeachExpr: false + narrowForeachBodyNonEmpty: false curlSetOptArrayTypes: false magicDirInInclude: false checkDateIntervalConstructor: false diff --git a/conf/parametersSchema.neon b/conf/parametersSchema.neon index 62dab9d9c3b..c5b594e04c3 100644 --- a/conf/parametersSchema.neon +++ b/conf/parametersSchema.neon @@ -44,6 +44,7 @@ parametersSchema: rawMessageInBaseline: bool() reportNestedTooWideType: bool() assignToByRefForeachExpr: bool() + narrowForeachBodyNonEmpty: bool() curlSetOptArrayTypes: bool() magicDirInInclude: bool() checkDateIntervalConstructor: bool() diff --git a/src/Analyser/StmtHandler/ForeachHandler.php b/src/Analyser/StmtHandler/ForeachHandler.php index 2ba5323bf7b..684cf46b3e6 100644 --- a/src/Analyser/StmtHandler/ForeachHandler.php +++ b/src/Analyser/StmtHandler/ForeachHandler.php @@ -90,6 +90,8 @@ public function __construct( private AssignHandler $assignHandler, #[AutowiredParameter] private bool $polluteScopeWithAlwaysIterableForeach, + #[AutowiredParameter(ref: '%featureToggles.narrowForeachBodyNonEmpty%')] + private bool $narrowForeachBodyNonEmpty, ) { } @@ -156,9 +158,12 @@ public function processStmt( // the "iteratee !== []" narrowing every loop pass merges in - composed // once from the iteratee's result (the same sentinel comparison the // walked synthetic would delegate to); the walk is the composition's - // miss seam + // miss seam. The loop body is only entered when the iteratee is + // non-empty; narrowForeachBodyNonEmpty narrows it there (list to + // non-empty-list, array to non-empty-array) even with + // polluteScopeWithAlwaysIterableForeach off. $nonEmptyIterateeScope = $scope; - if ($this->polluteScopeWithAlwaysIterableForeach) { + if ($this->narrowForeachBodyNonEmpty || $this->polluteScopeWithAlwaysIterableForeach) { $identicalNarrowingHelper = $this->container->getByType(IdenticalNarrowingHelper::class); $emptyArrayType = new ConstantArrayType([], []); $nonEmptyTypes = $identicalNarrowingHelper->specifyIdenticalAgainstType( diff --git a/tests/PHPStan/Analyser/Bug13312NoPolluteTest.php b/tests/PHPStan/Analyser/Bug13312NoPolluteTest.php new file mode 100644 index 00000000000..43f89c0d887 --- /dev/null +++ b/tests/PHPStan/Analyser/Bug13312NoPolluteTest.php @@ -0,0 +1,36 @@ +assertFileAsserts($assertType, $file, ...$args); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/bug-13312-no-pollute.neon', + ]; + } + +} diff --git a/tests/PHPStan/Analyser/Bug13312StableTest.php b/tests/PHPStan/Analyser/Bug13312StableTest.php new file mode 100644 index 00000000000..ff63e16c50b --- /dev/null +++ b/tests/PHPStan/Analyser/Bug13312StableTest.php @@ -0,0 +1,36 @@ +assertFileAsserts($assertType, $file, ...$args); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/bug-13312-stable.neon', + ]; + } + +} diff --git a/tests/PHPStan/Analyser/bug-13312-no-pollute.neon b/tests/PHPStan/Analyser/bug-13312-no-pollute.neon new file mode 100644 index 00000000000..2827569d0e9 --- /dev/null +++ b/tests/PHPStan/Analyser/bug-13312-no-pollute.neon @@ -0,0 +1,4 @@ +parameters: + polluteScopeWithAlwaysIterableForeach: false + featureToggles: + narrowForeachBodyNonEmpty: true diff --git a/tests/PHPStan/Analyser/bug-13312-stable.neon b/tests/PHPStan/Analyser/bug-13312-stable.neon new file mode 100644 index 00000000000..3ee516d3be6 --- /dev/null +++ b/tests/PHPStan/Analyser/bug-13312-stable.neon @@ -0,0 +1,2 @@ +parameters: + polluteScopeWithAlwaysIterableForeach: false diff --git a/tests/PHPStan/Analyser/data/bug-13312-no-pollute.php b/tests/PHPStan/Analyser/data/bug-13312-no-pollute.php new file mode 100644 index 00000000000..2926f972972 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-13312-no-pollute.php @@ -0,0 +1,37 @@ + $arr */ +function foo(array $arr): void { + assertType('list', $arr); + foreach ($arr as $v) { + assertType('non-empty-list', $arr); + } + assertType('list', $arr); + + for ($i = 0; $i < count($arr); ++$i) { + assertType('non-empty-list', $arr); + } + assertType('list', $arr); +} + +/** @param array $arr */ +function fooStringKeyed(array $arr): void { + assertType('array', $arr); + foreach ($arr as $v) { + assertType('non-empty-array', $arr); + } + assertType('array', $arr); +} + +/** @param list $arr */ +function fooReassign(array $arr): void { + foreach ($arr as $v) { + $arr = []; + assertType('array{}', $arr); + } + assertType('array{}', $arr); +} diff --git a/tests/PHPStan/Analyser/data/bug-13312-stable.php b/tests/PHPStan/Analyser/data/bug-13312-stable.php new file mode 100644 index 00000000000..3b952600eaf --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-13312-stable.php @@ -0,0 +1,32 @@ + $arr */ +function foo(array $arr): void { + assertType('list', $arr); + foreach ($arr as $v) { + assertType('list', $arr); + } + assertType('list', $arr); + + for ($i = 0; $i < count($arr); ++$i) { + assertType('non-empty-list', $arr); + } + assertType('list', $arr); +} + +/** @param array $arr */ +function fooStringKeyed(array $arr): void { + assertType('array', $arr); + foreach ($arr as $v) { + assertType('array', $arr); + } + assertType('array', $arr); +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-13312.php b/tests/PHPStan/Analyser/nsrt/bug-13312.php index 3a7ab22873c..9a47594a06f 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-13312.php +++ b/tests/PHPStan/Analyser/nsrt/bug-13312.php @@ -32,6 +32,24 @@ function foo(array $arr): void { } +/** @param array $arr */ +function fooStringKeyed(array $arr): void { + assertType('array', $arr); + foreach ($arr as $v) { + assertType('non-empty-array', $arr); + } + assertType('array', $arr); +} + +/** @param list $arr */ +function fooReassign(array $arr): void { + foreach ($arr as $v) { + $arr = []; + assertType('array{}', $arr); + } + assertType('array{}', $arr); +} + function fooBar(mixed $mixed): void { assertType('mixed', $mixed); foreach ($mixed as $v) { diff --git a/turbo-ext/src/ForeachHandler.cpp b/turbo-ext/src/ForeachHandler.cpp index 93d6fd6990c..52892820cf7 100644 --- a/turbo-ext/src/ForeachHandler.cpp +++ b/turbo-ext/src/ForeachHandler.cpp @@ -297,7 +297,7 @@ class ForeachHandler explicit ForeachHandler(zend_object *self) : self(self) {} /* the constructor body: the promoted properties */ - void construct(zval *container, bool implicitThrows, zval *varAnnotationProcessor, zval *assignHandler, bool polluteScopeWithAlwaysIterableForeach) + void construct(zval *container, bool implicitThrows, zval *varAnnotationProcessor, zval *assignHandler, bool polluteScopeWithAlwaysIterableForeach, bool narrowForeachBodyNonEmpty) { zv::ObjRef object(self); object.propAtWrite(slots::container, zv::Val::copyOf(zv::Ref(container))); @@ -305,6 +305,7 @@ class ForeachHandler object.propAtWrite(slots::varAnnotationProcessor, zv::Val::copyOf(zv::Ref(varAnnotationProcessor))); object.propAtWrite(slots::assignHandler, zv::Val::copyOf(zv::Ref(assignHandler))); object.propAtWrite(slots::polluteScopeWithAlwaysIterableForeach, zv::Val::boolean(polluteScopeWithAlwaysIterableForeach)); + object.propAtWrite(slots::narrowForeachBodyNonEmpty, zv::Val::boolean(narrowForeachBodyNonEmpty)); } /* Mirrors supports(); false = pending exception */ @@ -407,9 +408,12 @@ class ForeachHandler // the "iteratee !== []" narrowing every loop pass merges in - composed // once from the iteratee's result (the same sentinel comparison the // walked synthetic would delegate to); the walk is the composition's - // miss seam + // miss seam. The loop body is only entered when the iteratee is + // non-empty; narrowForeachBodyNonEmpty narrows it there (list to + // non-empty-list, array to non-empty-array) even with + // polluteScopeWithAlwaysIterableForeach off. zv::Val nonEmptyIterateeScope = zv::Val::copyOf(scope.ref()); - if (polluteScopeWithAlwaysIterableForeach) { + if (ptlh::boolSlot(self, slots::narrowForeachBodyNonEmpty) || polluteScopeWithAlwaysIterableForeach) { zv::Val identicalNarrowingHelper = ptlh::containerGetByType(OBJ_PROP_NUM(self, slots::container), pt_feh_identical_narrowing_helper_class); if (UNEXPECTED(identicalNarrowingHelper.isUndef())) return zv::Val(); if (UNEXPECTED(Z_TYPE_P(identicalNarrowingHelper.raw()) != IS_OBJECT)) { @@ -2006,9 +2010,9 @@ PT_MINIT_REGISTRATION(pt_register_foreach_handler) * container autowires the service by reflecting the constructor */ cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zval *container, *varAnnotationProcessor, *assignHandler; - bool implicitThrows, polluteScopeWithAlwaysIterableForeach; - if (!zp::parse(execute_data, container, implicitThrows, varAnnotationProcessor, assignHandler, polluteScopeWithAlwaysIterableForeach)) RETURN_THROWS(); - ForeachHandler(Z_OBJ_P(ZEND_THIS)).construct(container, implicitThrows, varAnnotationProcessor, assignHandler, polluteScopeWithAlwaysIterableForeach); + bool implicitThrows, polluteScopeWithAlwaysIterableForeach, narrowForeachBodyNonEmpty; + if (!zp::parse(execute_data, container, implicitThrows, varAnnotationProcessor, assignHandler, polluteScopeWithAlwaysIterableForeach, narrowForeachBodyNonEmpty)) RETURN_THROWS(); + ForeachHandler(Z_OBJ_P(ZEND_THIS)).construct(container, implicitThrows, varAnnotationProcessor, assignHandler, polluteScopeWithAlwaysIterableForeach, narrowForeachBodyNonEmpty); }); cls.method<&ForeachHandler::supports, zp::Obj>(sigs::supports); diff --git a/turbo-ext/src/generated/ForeachHandler.h b/turbo-ext/src/generated/ForeachHandler.h index f228c68593b..f08b71adc2d 100644 --- a/turbo-ext/src/generated/ForeachHandler.h +++ b/turbo-ext/src/generated/ForeachHandler.h @@ -15,6 +15,7 @@ inline constexpr uint32_t implicitThrows = 1; inline constexpr uint32_t varAnnotationProcessor = 2; inline constexpr uint32_t assignHandler = 3; inline constexpr uint32_t polluteScopeWithAlwaysIterableForeach = 4; +inline constexpr uint32_t narrowForeachBodyNonEmpty = 5; } // namespace slot inline void declareClass(reg::Class &cls) @@ -31,6 +32,7 @@ inline void declareProperties(reg::Class &cls) cls.property("varAnnotationProcessor", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\VarAnnotationProcessor"); cls.property("assignHandler", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExprHandler\\AssignHandler"); cls.property("polluteScopeWithAlwaysIterableForeach", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_BOOL); + cls.property("narrowForeachBodyNonEmpty", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_BOOL); } /* the string and parameter tables the signatures below index into (see reg::Sig) */ @@ -44,92 +46,94 @@ inline constexpr char strings[] = "assignHandler\0" /* 126 */ "PHPStan\\Analyser\\ExprHandler\\AssignHandler\0" /* 140 */ "polluteScopeWithAlwaysIterableForeach\0" /* 183 */ - "__construct\0" /* 221 */ - "stmt\0" /* 233 */ - "PhpParser\\Node\\Stmt\0" /* 238 */ - "\0" /* 258 */ - "supports\0" /* 259 */ - "nodeScopeResolver\0" /* 268 */ - "PHPStan\\Analyser\\NodeScopeResolver\0" /* 286 */ - "scope\0" /* 321 */ - "PHPStan\\Analyser\\MutatingScope\0" /* 327 */ - "storage\0" /* 358 */ - "PHPStan\\Analyser\\ExpressionResultStorage\0" /* 366 */ - "nodeCallback\0" /* 407 */ - "context\0" /* 420 */ - "PHPStan\\Analyser\\StatementContext\0" /* 428 */ - "PHPStan\\Analyser\\InternalStatementResult\0" /* 462 */ - "processStmt\0" /* 503 */ - "originalScope\0" /* 515 */ - "PhpParser\\Node\\Stmt\\Foreach_\0" /* 529 */ - "iterateeType\0" /* 558 */ - "PHPStan\\Type\\Type\0" /* 571 */ - "nativeIterateeType\0" /* 589 */ - "enterForeach\0" /* 608 */ - "originalStorage\0" /* 621 */ - "tryProcessUnrolledConstantArrayForeach\0" /* 637 */ - "iteratee\0" /* 676 */ - "PhpParser\\Node\\Expr\0" /* 685 */ - "exprType\0" /* 705 */ - "PHPStan\\Analyser\\InternalThrowPoint\0" /* 714 */ - "getTraversableForeachThrowPoint\0" /* 750 */ - "iterableValueType\0" /* 782 */ - "list\0" /* 800 */ - "PhpParser\\Node\\Expr\\List_\0" /* 805 */ - "addDestructureTaggedUnionConditionalHolders"; /* 831 */ + "narrowForeachBodyNonEmpty\0" /* 221 */ + "__construct\0" /* 247 */ + "stmt\0" /* 259 */ + "PhpParser\\Node\\Stmt\0" /* 264 */ + "\0" /* 284 */ + "supports\0" /* 285 */ + "nodeScopeResolver\0" /* 294 */ + "PHPStan\\Analyser\\NodeScopeResolver\0" /* 312 */ + "scope\0" /* 347 */ + "PHPStan\\Analyser\\MutatingScope\0" /* 353 */ + "storage\0" /* 384 */ + "PHPStan\\Analyser\\ExpressionResultStorage\0" /* 392 */ + "nodeCallback\0" /* 433 */ + "context\0" /* 446 */ + "PHPStan\\Analyser\\StatementContext\0" /* 454 */ + "PHPStan\\Analyser\\InternalStatementResult\0" /* 488 */ + "processStmt\0" /* 529 */ + "originalScope\0" /* 541 */ + "PhpParser\\Node\\Stmt\\Foreach_\0" /* 555 */ + "iterateeType\0" /* 584 */ + "PHPStan\\Type\\Type\0" /* 597 */ + "nativeIterateeType\0" /* 615 */ + "enterForeach\0" /* 634 */ + "originalStorage\0" /* 647 */ + "tryProcessUnrolledConstantArrayForeach\0" /* 663 */ + "iteratee\0" /* 702 */ + "PhpParser\\Node\\Expr\0" /* 711 */ + "exprType\0" /* 731 */ + "PHPStan\\Analyser\\InternalThrowPoint\0" /* 740 */ + "getTraversableForeachThrowPoint\0" /* 776 */ + "iterableValueType\0" /* 808 */ + "list\0" /* 826 */ + "PhpParser\\Node\\Expr\\List_\0" /* 831 */ + "addDestructureTaggedUnionConditionalHolders"; /* 857 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 10), /* __construct $container */ reg::packed(48, MAY_BE_BOOL), /* __construct $implicitThrows */ reg::packed(63, 0, 86), /* __construct $varAnnotationProcessor */ reg::packed(126, 0, 140), /* __construct $assignHandler */ reg::packed(183, MAY_BE_BOOL), /* __construct $polluteScopeWithAlwaysIterableForeach */ - reg::packed(233, 0, 238), /* supports $stmt */ - reg::packed(258, MAY_BE_BOOL), /* supports return */ - reg::packed(268, 0, 286), /* processStmt $nodeScopeResolver */ - reg::packed(233, 0, 238), /* processStmt $stmt */ - reg::packed(321, 0, 327), /* processStmt $scope */ - reg::packed(358, 0, 366), /* processStmt $storage */ - reg::packed(407, MAY_BE_CALLABLE), /* processStmt $nodeCallback */ - reg::packed(420, 0, 428), /* processStmt $context */ - reg::packed(258, 0, 462), /* processStmt return */ - reg::packed(268, 0, 286), /* enterForeach $nodeScopeResolver */ - reg::packed(321, 0, 327), /* enterForeach $scope */ - reg::packed(358, 0, 366), /* enterForeach $storage */ - reg::packed(515, 0, 327), /* enterForeach $originalScope */ - reg::packed(233, 0, 529), /* enterForeach $stmt */ - reg::packed(558, 0, 571), /* enterForeach $iterateeType */ - reg::packed(589, 0, 571), /* enterForeach $nativeIterateeType */ - reg::packed(407, MAY_BE_CALLABLE), /* enterForeach $nodeCallback */ - reg::packed(258, 0, 327), /* enterForeach return */ - reg::packed(268, 0, 286), /* tryProcessUnrolledConstantArrayForeach $nodeScopeResolver */ - reg::packed(233, 0, 529), /* tryProcessUnrolledConstantArrayForeach $stmt */ - reg::packed(515, 0, 327), /* tryProcessUnrolledConstantArrayForeach $originalScope */ - reg::packed(621, 0, 366), /* tryProcessUnrolledConstantArrayForeach $originalStorage */ - reg::packed(420, 0, 428), /* tryProcessUnrolledConstantArrayForeach $context */ - reg::packed(558, 0, 571), /* tryProcessUnrolledConstantArrayForeach $iterateeType */ - reg::packed(589, 0, 571), /* tryProcessUnrolledConstantArrayForeach $nativeIterateeType */ - reg::packed(258, MAY_BE_NULL | MAY_BE_ARRAY), /* tryProcessUnrolledConstantArrayForeach return */ - reg::packed(321, 0, 327), /* getTraversableForeachThrowPoint $scope */ - reg::packed(676, 0, 685), /* getTraversableForeachThrowPoint $iteratee */ - reg::packed(705, 0, 571), /* getTraversableForeachThrowPoint $exprType */ - reg::packed(258, MAY_BE_NULL, 714), /* getTraversableForeachThrowPoint return */ - reg::packed(321, 0, 327), /* addDestructureTaggedUnionConditionalHolders $scope */ - reg::packed(782, 0, 571), /* addDestructureTaggedUnionConditionalHolders $iterableValueType */ - reg::packed(800, 0, 805), /* addDestructureTaggedUnionConditionalHolders $list */ - reg::packed(258, 0, 327), /* addDestructureTaggedUnionConditionalHolders return */ + reg::packed(221, MAY_BE_BOOL), /* __construct $narrowForeachBodyNonEmpty */ + reg::packed(259, 0, 264), /* supports $stmt */ + reg::packed(284, MAY_BE_BOOL), /* supports return */ + reg::packed(294, 0, 312), /* processStmt $nodeScopeResolver */ + reg::packed(259, 0, 264), /* processStmt $stmt */ + reg::packed(347, 0, 353), /* processStmt $scope */ + reg::packed(384, 0, 392), /* processStmt $storage */ + reg::packed(433, MAY_BE_CALLABLE), /* processStmt $nodeCallback */ + reg::packed(446, 0, 454), /* processStmt $context */ + reg::packed(284, 0, 488), /* processStmt return */ + reg::packed(294, 0, 312), /* enterForeach $nodeScopeResolver */ + reg::packed(347, 0, 353), /* enterForeach $scope */ + reg::packed(384, 0, 392), /* enterForeach $storage */ + reg::packed(541, 0, 353), /* enterForeach $originalScope */ + reg::packed(259, 0, 555), /* enterForeach $stmt */ + reg::packed(584, 0, 597), /* enterForeach $iterateeType */ + reg::packed(615, 0, 597), /* enterForeach $nativeIterateeType */ + reg::packed(433, MAY_BE_CALLABLE), /* enterForeach $nodeCallback */ + reg::packed(284, 0, 353), /* enterForeach return */ + reg::packed(294, 0, 312), /* tryProcessUnrolledConstantArrayForeach $nodeScopeResolver */ + reg::packed(259, 0, 555), /* tryProcessUnrolledConstantArrayForeach $stmt */ + reg::packed(541, 0, 353), /* tryProcessUnrolledConstantArrayForeach $originalScope */ + reg::packed(647, 0, 392), /* tryProcessUnrolledConstantArrayForeach $originalStorage */ + reg::packed(446, 0, 454), /* tryProcessUnrolledConstantArrayForeach $context */ + reg::packed(584, 0, 597), /* tryProcessUnrolledConstantArrayForeach $iterateeType */ + reg::packed(615, 0, 597), /* tryProcessUnrolledConstantArrayForeach $nativeIterateeType */ + reg::packed(284, MAY_BE_NULL | MAY_BE_ARRAY), /* tryProcessUnrolledConstantArrayForeach return */ + reg::packed(347, 0, 353), /* getTraversableForeachThrowPoint $scope */ + reg::packed(702, 0, 711), /* getTraversableForeachThrowPoint $iteratee */ + reg::packed(731, 0, 597), /* getTraversableForeachThrowPoint $exprType */ + reg::packed(284, MAY_BE_NULL, 740), /* getTraversableForeachThrowPoint return */ + reg::packed(347, 0, 353), /* addDestructureTaggedUnionConditionalHolders $scope */ + reg::packed(808, 0, 597), /* addDestructureTaggedUnionConditionalHolders $iterableValueType */ + reg::packed(826, 0, 831), /* addDestructureTaggedUnionConditionalHolders $list */ + reg::packed(284, 0, 353), /* addDestructureTaggedUnionConditionalHolders 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 = { { 221 /* __construct */, 5, 0, 5, reg::NoArg, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig supports = { { 259 /* supports */, 1, 5, 1, 6, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig processStmt = { { 503 /* processStmt */, 6, 7, 6, 13, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig enterForeach = { { 608 /* enterForeach */, 8, 14, 8, 22, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig tryProcessUnrolledConstantArrayForeach = { { 637 /* tryProcessUnrolledConstantArrayForeach */, 7, 23, 7, 30, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getTraversableForeachThrowPoint = { { 750 /* getTraversableForeachThrowPoint */, 3, 31, 3, 34, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig addDestructureTaggedUnionConditionalHolders = { { 831 /* addDestructureTaggedUnionConditionalHolders */, 3, 35, 3, 38, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig __construct = { { 247 /* __construct */, 6, 0, 6, reg::NoArg, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig supports = { { 285 /* supports */, 1, 6, 1, 7, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig processStmt = { { 529 /* processStmt */, 6, 8, 6, 14, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig enterForeach = { { 634 /* enterForeach */, 8, 15, 8, 23, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig tryProcessUnrolledConstantArrayForeach = { { 663 /* tryProcessUnrolledConstantArrayForeach */, 7, 24, 7, 31, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getTraversableForeachThrowPoint = { { 776 /* getTraversableForeachThrowPoint */, 3, 32, 3, 35, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig addDestructureTaggedUnionConditionalHolders = { { 857 /* addDestructureTaggedUnionConditionalHolders */, 3, 36, 3, 39, ZEND_ACC_PRIVATE } }; } // namespace sig } // namespace ptdecl::ForeachHandler