From d20e3a649e54cfa618860bdace9594e9dcd852da Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 23 Oct 2026 17:10:09 +0200 Subject: [PATCH] Keep an explicit key of an unsealed shape as an optional never key when subtracting its offset ConstantArrayType::tryRemove() subtracted HasOffsetType and HasOffsetValueType by unsetting the offset. On an unsealed shape whose extras can hold that offset, dropping the explicit key lets the extras supply it again with their value type, so the "subtracted" shape is wider than the original: array{a?: string, ...} minus hasOffset('a') was array{...}, and array{a?: string, b: int, ...} re-admitted 'a' as int. The falsey isset() narrowing from 77527df087 and the falsey array_key_exists() narrowing both go through this subtraction, and merging the widened branch back collapsed the subject to array. The key now stays as an optional never key instead - absent, but still fencing the offset off from the extras - and branch merges restore the original shape. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Xj1fBpXNVT34mRk1GQBxJF --- src/Type/Constant/ConstantArrayType.php | 68 +++++--- tests/PHPStan/Analyser/nsrt/bug-15428.php | 74 +++++++++ .../ParameterOutAssignedTypeRuleTest.php | 5 + .../Rules/Variables/data/bug-15428.php | 12 ++ turbo-ext/src/ConstantArrayType.cpp | 67 +++++++- turbo-ext/src/generated/ConstantArrayType.h | 148 +++++++++--------- 6 files changed, 279 insertions(+), 95 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-15428.php create mode 100644 tests/PHPStan/Rules/Variables/data/bug-15428.php diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 85e802ecfb6..a198e2161a7 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -54,6 +54,7 @@ use PHPStan\Type\IsSuperTypeOfResult; use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; +use PHPStan\Type\NonAcceptingNeverType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\RecursionGuard; @@ -2793,15 +2794,7 @@ public function tryRemove(Type $typeToRemove): ?Type } if ($valueIsSuperType->yes()) { - $unsetResult = $this->unsetOffset($offsetType, true); - // When the source was definitely a list but the post-unset shape - // definitely isn't (e.g. unsetting a non-optional leading key - // creates a hole), no value of $this could have lacked the - // removed key — the subtraction yields the empty set. - if ($this->isList->yes() && $unsetResult->isList()->no()) { - return new NeverType(); - } - return $unsetResult; + return $this->removeOffset($offsetType); } $newValueType = TypeCombinator::remove($currentValueType, $valueTypeToRemove); @@ -2822,20 +2815,59 @@ public function tryRemove(Type $typeToRemove): ?Type } if ($typeToRemove instanceof HasOffsetType) { - $unsetResult = $this->unsetOffset($typeToRemove->getOffsetType(), true); - // When the source was definitely a list but the post-unset shape - // definitely isn't (e.g. unsetting a non-optional leading key - // creates a hole), no value of $this could have lacked the - // removed key — the subtraction yields the empty set. - if ($this->isList->yes() && $unsetResult->isList()->no()) { - return new NeverType(); - } - return $unsetResult; + return $this->removeOffset($typeToRemove->getOffsetType()); } return null; } + /** + * The values of this shape lacking the offset. Unlike unsetOffset(), an + * explicit key the unsealed extras can also hold becomes an optional never + * key - dropping it would let the extras supply it again, widening the + * shape instead of subtracting from it. + */ + private function removeOffset(Type $offsetType): Type + { + $unsetResult = $this->unsetOffset($offsetType, true); + // When the source was definitely a list but the post-unset shape + // definitely isn't (e.g. unsetting a non-optional leading key + // creates a hole), no value of $this could have lacked the + // removed key — the subtraction yields the empty set. + if ($this->isList->yes() && $unsetResult->isList()->no()) { + return new NeverType(); + } + + if ($this->unsealed === null || !$this->isUnsealed()->yes()) { + return $unsetResult; + } + + $offsetType = $offsetType->toArrayKey(); + $offsetValues = $offsetType->getConstantScalarValues(); + if (count($offsetValues) !== 1 || $this->unsealed[0]->isSuperTypeOf($offsetType)->no()) { + return $unsetResult; + } + + $offsetValue = $offsetValues[0]; + if (!is_int($offsetValue) && !is_string($offsetValue)) { + return $unsetResult; + } + + $i = $this->getKeyIndexMap()[$offsetValue] ?? null; + if ($i === null) { + return $unsetResult; + } + + $valueTypes = $this->valueTypes; + $valueTypes[$i] = new NonAcceptingNeverType(); + $optionalKeys = $this->optionalKeys; + if (!in_array($i, $optionalKeys, true)) { + $optionalKeys[] = $i; + } + + return $this->recreate($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $optionalKeys, $unsetResult->isList(), $this->unsealed); + } + public function traverse(callable $cb): Type { $valueTypes = []; diff --git a/tests/PHPStan/Analyser/nsrt/bug-15428.php b/tests/PHPStan/Analyser/nsrt/bug-15428.php new file mode 100644 index 00000000000..0298cc1f79b --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-15428.php @@ -0,0 +1,74 @@ +} $c + */ +function typedExtras(array $c): void +{ + if (!isset($c['a'])) { + assertType('array{a?: never, b: int, ...}', $c); + } + assertType('array{a?: string, b: int, ...}', $c); +} + +/** + * @param array{b: int, ...} $c + */ +function implicitKey(array $c): void +{ + if (!isset($c['a'])) { + assertType('array{b: int, ...}', $c); + } + assertType('array{b: int, ...}', $c); +} + +/** + * @param array{a?: string, ...} $c + */ +function extrasCannotHoldKey(array $c): void +{ + if (!isset($c['a'])) { + assertType('array{...}', $c); + } + assertType('array{a?: string, ...}', $c); +} diff --git a/tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php b/tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php index ed7fb791ddb..9a6dd9e6354 100644 --- a/tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php +++ b/tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php @@ -140,4 +140,9 @@ public function testBug15066(): void ]); } + public function testBug15428(): void + { + $this->analyse([__DIR__ . '/data/bug-15428.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-15428.php b/tests/PHPStan/Rules/Variables/data/bug-15428.php new file mode 100644 index 00000000000..9eb3aba793a --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-15428.php @@ -0,0 +1,12 @@ +unsetOffset($offsetType, true), never when that turns a * definite list into a definite non-list; UNDEF = pending exception */ - zv::Val unsetPreservingListCertainty(zval *offsetType, zval *isListSlotValue) const + /* removeOffset(): the values of $this lacking the offset — an explicit + * key the unsealed extras can also hold stays as an optional never key + * instead of being dropped; UNDEF = pending exception */ + zv::Val removeOffset(zval *offsetTypeArg, zval *isListSlotValue) const { - zv::Val unsetResult = thisUnsetOffset(offsetType, true); + zv::Val unsetResult = thisUnsetOffset(offsetTypeArg, true); if (UNEXPECTED(unsetResult.isUndef())) return zv::Val(); /* When the source was definitely a list but the post-unset shape * definitely isn't (e.g. unsetting a non-optional leading key @@ -6636,7 +6639,61 @@ class ConstantArrayType if (UNEXPECTED(resultIsList < 0)) return zv::Val(); if (resultIsList == PT_TRI_NO) return neverType(); } - return unsetResult; + + /* $this->unsealed === null || !$this->isUnsealed()->yes() */ + zval *u = unsealedSlot(); + if (UNEXPECTED(u == NULL)) return zv::Val(); + if (Z_TYPE_P(u) == IS_NULL) return unsetResult; + zend_long unsealedness = thisIsUnsealed(); + if (UNEXPECTED(unsealedness < 0)) return zv::Val(); + if (unsealedness != PT_TRI_YES) return unsetResult; + + zv::Val offsetType = callType(Z_OBJ_P(offsetTypeArg), PT_LC("toarraykey"), 0, NULL); + if (UNEXPECTED(offsetType.isUndef())) return zv::Val(); + zv::Val offsetValues = pt_type_call_array(Z_OBJ_P(offsetType.raw()), PT_LC("getconstantscalarvalues"), 0, NULL); + if (UNEXPECTED(offsetValues.isUndef())) return zv::Val(); + if (arrayCount(offsetValues.raw()) != 1) return unsetResult; + zval *unsealedKeyType, *unsealedValueType; + if (UNEXPECTED(!unsealedPair(u, unsealedKeyType, unsealedValueType))) return zv::Val(); + zend_long extrasHoldOffset = isSuperTypeOfValue(unsealedKeyType, offsetType.raw()); + if (UNEXPECTED(extrasHoldOffset < 0)) return zv::Val(); + if (extrasHoldOffset == PT_TRI_NO) return unsetResult; + + /* $offsetValues[0], an int or a string */ + zval *offsetValue = zend_hash_index_find(Z_ARRVAL_P(offsetValues.raw()), 0); + if (offsetValue == NULL) return unsetResult; + ZVAL_DEREF(offsetValue); + if (Z_TYPE_P(offsetValue) != IS_LONG && Z_TYPE_P(offsetValue) != IS_STRING) return unsetResult; + + /* $this->getKeyIndexMap()[$offsetValue] ?? null */ + zv::Val map = getKeyIndexMap(); + if (UNEXPECTED(map.isUndef())) return zv::Val(); + zval *found = mapFind(Z_ARRVAL_P(map.raw()), offsetValue); + if (found == NULL || Z_TYPE_P(found) != IS_LONG) return unsetResult; + zend_long i = Z_LVAL_P(found); + + zval *k = keyTypes(); + zval *v = k != NULL ? valueTypes() : NULL; + zval *n = v != NULL ? nextAutoIndexes() : NULL; + zval *o = n != NULL ? optionalKeys() : NULL; + if (UNEXPECTED(o == NULL)) return zv::Val(); + + zval never; + if (UNEXPECTED(!pt_non_accepting_never_type_new(&never))) return zv::Val(); + zv::Val neverValue = zv::Val::adopt(never); + zv::Arr valueTypesCopy = zv::Arr::adoptVal(zv::Val::copyOf(zv::Ref(v))); + valueTypesCopy.separate(); + zv::ArrRef(valueTypesCopy.raw()).setIndex((zend_ulong) i, zv::Ref(neverValue.raw())); + + zv::Arr optionalKeysCopy = zv::Arr::adoptVal(zv::Val::copyOf(zv::Ref(o))); + if (!inArrayStrictLong(o, i)) { + optionalKeysCopy.separate(); + optionalKeysCopy.push(zv::Val::integer(i)); + } + + zend_long resultIsList = pt_type_op_trinary(Z_OBJ_P(unsetResult.raw()), PT_OP_IS_LIST, 0, NULL); + if (UNEXPECTED(resultIsList < 0)) return zv::Val(); + return thisRecreate(k, valueTypesCopy.raw(), n, optionalKeysCopy.raw(), pt_trinary_singleton(resultIsList), u); } /* TypeCombinator::intersect($this, new NonEmptyArrayType()) */ diff --git a/turbo-ext/src/generated/ConstantArrayType.h b/turbo-ext/src/generated/ConstantArrayType.h index a459141f295..a3a9643fc28 100644 --- a/turbo-ext/src/generated/ConstantArrayType.h +++ b/turbo-ext/src/generated/ConstantArrayType.h @@ -190,38 +190,39 @@ inline constexpr char strings[] = "getReferencedTemplateTypes\0" /* 2053 */ "typeToRemove\0" /* 2080 */ "tryRemove\0" /* 2093 */ - "cb\0" /* 2103 */ - "traverse\0" /* 2106 */ - "right\0" /* 2115 */ - "traverseSimultaneously\0" /* 2121 */ - "otherArray\0" /* 2144 */ - "isKeysSupersetOf\0" /* 2155 */ - "legacyIsKeysSupersetOf\0" /* 2172 */ - "mergeWith\0" /* 2195 */ - "legacyMergeWith\0" /* 2205 */ - "getKeyIndexMap\0" /* 2221 */ - "findVerifiedKeyIndex\0" /* 2236 */ - "otherKeyType\0" /* 2257 */ - "getKeyIndex\0" /* 2270 */ - "makeOffsetRequired\0" /* 2282 */ - "makeList\0" /* 2301 */ - "makeListMaybe\0" /* 2310 */ - "mapValueType\0" /* 2324 */ - "mapKeyType\0" /* 2337 */ - "makeAllArrayKeysOptional\0" /* 2348 */ - "case\0" /* 2373 */ - "changeKeyCaseArray\0" /* 2378 */ - "filterArrayRemovingFalsey\0" /* 2397 */ - "PHPStan\\Type\\Constant\\ConstantStringType\0" /* 2423 */ - "foldConstantStringKeyCase\0" /* 2464 */ - "key\0" /* 2490 */ - "foldUnsealedKeyCase\0" /* 2494 */ - "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 2514 */ - "toPhpDocNode\0" /* 2553 */ - "value\0" /* 2566 */ - "isValidIdentifier\0" /* 2572 */ - "getFiniteTypes\0" /* 2590 */ - "hasTemplateOrLateResolvableType"; /* 2605 */ + "removeOffset\0" /* 2103 */ + "cb\0" /* 2116 */ + "traverse\0" /* 2119 */ + "right\0" /* 2128 */ + "traverseSimultaneously\0" /* 2134 */ + "otherArray\0" /* 2157 */ + "isKeysSupersetOf\0" /* 2168 */ + "legacyIsKeysSupersetOf\0" /* 2185 */ + "mergeWith\0" /* 2208 */ + "legacyMergeWith\0" /* 2218 */ + "getKeyIndexMap\0" /* 2234 */ + "findVerifiedKeyIndex\0" /* 2249 */ + "otherKeyType\0" /* 2270 */ + "getKeyIndex\0" /* 2283 */ + "makeOffsetRequired\0" /* 2295 */ + "makeList\0" /* 2314 */ + "makeListMaybe\0" /* 2323 */ + "mapValueType\0" /* 2337 */ + "mapKeyType\0" /* 2350 */ + "makeAllArrayKeysOptional\0" /* 2361 */ + "case\0" /* 2386 */ + "changeKeyCaseArray\0" /* 2391 */ + "filterArrayRemovingFalsey\0" /* 2410 */ + "PHPStan\\Type\\Constant\\ConstantStringType\0" /* 2436 */ + "foldConstantStringKeyCase\0" /* 2477 */ + "key\0" /* 2503 */ + "foldUnsealedKeyCase\0" /* 2507 */ + "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 2527 */ + "toPhpDocNode\0" /* 2566 */ + "value\0" /* 2579 */ + "isValidIdentifier\0" /* 2585 */ + "getFiniteTypes\0" /* 2603 */ + "hasTemplateOrLateResolvableType"; /* 2618 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_ARRAY), /* __construct $keyTypes */ reg::packed(9, MAY_BE_ARRAY), /* __construct $valueTypes */ @@ -368,44 +369,46 @@ inline constexpr reg::PackedArg args[] = { reg::packed(110, MAY_BE_ARRAY), /* getReferencedTemplateTypes return */ reg::packed(2080, 0, 254), /* tryRemove $typeToRemove */ reg::packed(110, MAY_BE_NULL, 254), /* tryRemove return */ - reg::packed(2103, MAY_BE_CALLABLE), /* traverse $cb */ + reg::packed(881, 0, 254), /* removeOffset $offsetType */ + reg::packed(110, 0, 254), /* removeOffset return */ + reg::packed(2116, MAY_BE_CALLABLE), /* traverse $cb */ reg::packed(110, 0, 254), /* traverse return */ - reg::packed(2115, 0, 254), /* traverseSimultaneously $right */ - reg::packed(2103, MAY_BE_CALLABLE), /* traverseSimultaneously $cb */ + reg::packed(2128, 0, 254), /* traverseSimultaneously $right */ + reg::packed(2116, MAY_BE_CALLABLE), /* traverseSimultaneously $cb */ reg::packed(110, 0, 254), /* traverseSimultaneously return */ - reg::packed(2144, 0, 148), /* isKeysSupersetOf $otherArray */ + reg::packed(2157, 0, 148), /* isKeysSupersetOf $otherArray */ reg::packed(110, MAY_BE_BOOL), /* isKeysSupersetOf return */ - reg::packed(2144, 0, 148), /* legacyIsKeysSupersetOf $otherArray */ + reg::packed(2157, 0, 148), /* legacyIsKeysSupersetOf $otherArray */ reg::packed(110, MAY_BE_BOOL), /* legacyIsKeysSupersetOf return */ - reg::packed(2144, 0, 148), /* mergeWith $otherArray */ + reg::packed(2157, 0, 148), /* mergeWith $otherArray */ reg::packed(110, 0, 148), /* mergeWith return */ - reg::packed(2144, 0, 148), /* legacyMergeWith $otherArray */ + reg::packed(2157, 0, 148), /* legacyMergeWith $otherArray */ reg::packed(110, 0, 148), /* legacyMergeWith return */ reg::packed(110, MAY_BE_ARRAY), /* getKeyIndexMap return */ reg::packed(881, 0, 254), /* findVerifiedKeyIndex $offsetType */ reg::packed(110, MAY_BE_NULL | MAY_BE_LONG), /* findVerifiedKeyIndex return */ - reg::packed(2257, 0), /* getKeyIndex $otherKeyType */ + reg::packed(2270, 0), /* getKeyIndex $otherKeyType */ reg::packed(110, MAY_BE_NULL | MAY_BE_LONG), /* getKeyIndex return */ reg::packed(881, 0, 254), /* makeOffsetRequired $offsetType */ reg::packed(110, 0, 148), /* makeOffsetRequired return */ reg::packed(110, 0, 254), /* makeList return */ reg::packed(110, 0, 254), /* makeListMaybe return */ - reg::packed(2103, MAY_BE_CALLABLE), /* mapValueType $cb */ + reg::packed(2116, MAY_BE_CALLABLE), /* mapValueType $cb */ reg::packed(110, 0, 254), /* mapValueType return */ - reg::packed(2103, MAY_BE_CALLABLE), /* mapKeyType $cb */ + reg::packed(2116, MAY_BE_CALLABLE), /* mapKeyType $cb */ reg::packed(110, 0, 254), /* mapKeyType return */ reg::packed(110, 0, 254), /* makeAllArrayKeysOptional return */ - reg::packed(2373, MAY_BE_NULL | MAY_BE_LONG), /* changeKeyCaseArray $case */ + reg::packed(2386, MAY_BE_NULL | MAY_BE_LONG), /* changeKeyCaseArray $case */ reg::packed(110, 0, 254), /* changeKeyCaseArray return */ reg::packed(110, 0, 254), /* filterArrayRemovingFalsey return */ - reg::packed(479, 0, 2423), /* foldConstantStringKeyCase $type */ - reg::packed(2373, MAY_BE_NULL | MAY_BE_LONG), /* foldConstantStringKeyCase $case */ + reg::packed(479, 0, 2436), /* foldConstantStringKeyCase $type */ + reg::packed(2386, MAY_BE_NULL | MAY_BE_LONG), /* foldConstantStringKeyCase $case */ reg::packed(110, 0, 254), /* foldConstantStringKeyCase return */ - reg::packed(2490, 0, 254), /* foldUnsealedKeyCase $key */ - reg::packed(2373, MAY_BE_NULL | MAY_BE_LONG), /* foldUnsealedKeyCase $case */ + reg::packed(2503, 0, 254), /* foldUnsealedKeyCase $key */ + reg::packed(2386, MAY_BE_NULL | MAY_BE_LONG), /* foldUnsealedKeyCase $case */ reg::packed(110, 0, 254), /* foldUnsealedKeyCase return */ - reg::packed(110, 0, 2514), /* toPhpDocNode return */ - reg::packed(2566, MAY_BE_STRING), /* isValidIdentifier $value */ + reg::packed(110, 0, 2527), /* toPhpDocNode return */ + reg::packed(2579, MAY_BE_STRING), /* isValidIdentifier $value */ reg::packed(110, MAY_BE_BOOL), /* isValidIdentifier return */ reg::packed(110, MAY_BE_ARRAY), /* getFiniteTypes return */ reg::packed(110, MAY_BE_BOOL), /* hasTemplateOrLateResolvableType return */ @@ -493,29 +496,30 @@ inline constexpr sigtab::Sig shouldBeDescribedAsAList = { { 1900 /* shouldBeDesc inline constexpr sigtab::Sig inferTemplateTypes = { { 1975 /* inferTemplateTypes */, 1, 139, 1, 140, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig getReferencedTemplateTypes = { { 2053 /* getReferencedTemplateTypes */, 1, 141, 1, 142, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig tryRemove = { { 2093 /* tryRemove */, 1, 143, 1, 144, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig traverse = { { 2106 /* traverse */, 1, 145, 1, 146, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig traverseSimultaneously = { { 2121 /* traverseSimultaneously */, 2, 147, 2, 149, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isKeysSupersetOf = { { 2155 /* isKeysSupersetOf */, 1, 150, 1, 151, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig legacyIsKeysSupersetOf = { { 2172 /* legacyIsKeysSupersetOf */, 1, 152, 1, 153, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig mergeWith = { { 2195 /* mergeWith */, 1, 154, 1, 155, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig legacyMergeWith = { { 2205 /* legacyMergeWith */, 1, 156, 1, 157, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getKeyIndexMap = { { 2221 /* getKeyIndexMap */, 0, 158, 0, 158, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig findVerifiedKeyIndex = { { 2236 /* findVerifiedKeyIndex */, 1, 159, 1, 160, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getKeyIndex = { { 2270 /* getKeyIndex */, 1, 161, 1, 162, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig makeOffsetRequired = { { 2282 /* makeOffsetRequired */, 1, 163, 1, 164, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig makeList = { { 2301 /* makeList */, 0, 165, 0, 165, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig makeListMaybe = { { 2310 /* makeListMaybe */, 0, 166, 0, 166, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig mapValueType = { { 2324 /* mapValueType */, 1, 167, 1, 168, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig mapKeyType = { { 2337 /* mapKeyType */, 1, 169, 1, 170, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig makeAllArrayKeysOptional = { { 2348 /* makeAllArrayKeysOptional */, 0, 171, 0, 171, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig changeKeyCaseArray = { { 2378 /* changeKeyCaseArray */, 1, 172, 1, 173, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig filterArrayRemovingFalsey = { { 2397 /* filterArrayRemovingFalsey */, 0, 174, 0, 174, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig foldConstantStringKeyCase = { { 2464 /* foldConstantStringKeyCase */, 2, 175, 2, 177, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig foldUnsealedKeyCase = { { 2494 /* foldUnsealedKeyCase */, 2, 178, 2, 180, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig toPhpDocNode = { { 2553 /* toPhpDocNode */, 0, 181, 0, 181, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isValidIdentifier = { { 2572 /* isValidIdentifier */, 1, 182, 1, 183, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig getFiniteTypes = { { 2590 /* getFiniteTypes */, 0, 184, 0, 184, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasTemplateOrLateResolvableType = { { 2605 /* hasTemplateOrLateResolvableType */, 0, 185, 0, 185, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig removeOffset = { { 2103 /* removeOffset */, 1, 145, 1, 146, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig traverse = { { 2119 /* traverse */, 1, 147, 1, 148, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig traverseSimultaneously = { { 2134 /* traverseSimultaneously */, 2, 149, 2, 151, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isKeysSupersetOf = { { 2168 /* isKeysSupersetOf */, 1, 152, 1, 153, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig legacyIsKeysSupersetOf = { { 2185 /* legacyIsKeysSupersetOf */, 1, 154, 1, 155, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig mergeWith = { { 2208 /* mergeWith */, 1, 156, 1, 157, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig legacyMergeWith = { { 2218 /* legacyMergeWith */, 1, 158, 1, 159, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getKeyIndexMap = { { 2234 /* getKeyIndexMap */, 0, 160, 0, 160, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig findVerifiedKeyIndex = { { 2249 /* findVerifiedKeyIndex */, 1, 161, 1, 162, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getKeyIndex = { { 2283 /* getKeyIndex */, 1, 163, 1, 164, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig makeOffsetRequired = { { 2295 /* makeOffsetRequired */, 1, 165, 1, 166, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig makeList = { { 2314 /* makeList */, 0, 167, 0, 167, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig makeListMaybe = { { 2323 /* makeListMaybe */, 0, 168, 0, 168, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig mapValueType = { { 2337 /* mapValueType */, 1, 169, 1, 170, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig mapKeyType = { { 2350 /* mapKeyType */, 1, 171, 1, 172, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig makeAllArrayKeysOptional = { { 2361 /* makeAllArrayKeysOptional */, 0, 173, 0, 173, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig changeKeyCaseArray = { { 2391 /* changeKeyCaseArray */, 1, 174, 1, 175, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig filterArrayRemovingFalsey = { { 2410 /* filterArrayRemovingFalsey */, 0, 176, 0, 176, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig foldConstantStringKeyCase = { { 2477 /* foldConstantStringKeyCase */, 2, 177, 2, 179, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig foldUnsealedKeyCase = { { 2507 /* foldUnsealedKeyCase */, 2, 180, 2, 182, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig toPhpDocNode = { { 2566 /* toPhpDocNode */, 0, 183, 0, 183, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isValidIdentifier = { { 2585 /* isValidIdentifier */, 1, 184, 1, 185, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig getFiniteTypes = { { 2603 /* getFiniteTypes */, 0, 186, 0, 186, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasTemplateOrLateResolvableType = { { 2618 /* hasTemplateOrLateResolvableType */, 0, 187, 0, 187, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ConstantArrayType