Repository navigation
Keep a merged call without a narrowable argument in the scope - #7064
Merged
Merged
Conversation
Since bcca2b0, a merge drops a remembered call whose merged type is what the call resolves to on its own, so that later narrowing of the call's arguments reaches it. A call without an argument that narrowing can change, such as isMulti() or can('update'), never goes stale, but dropping its entry still changes later merges: a branch without the entry merges into a Maybe holder, which derives no conditional guards and is intersected as if it held the call's type when a conditional expression narrows the call. Such calls now keep their entry. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d722-5ae1-772c-a909-1fc9b8e04be4
ondrejmirtes
force-pushed
the
fix-15727-keep-argumentless-merged-calls
branch
from
November 14, 2026 09:12
71ae3ab to
25004a0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes phpstan/phpstan#15727
Since bcca2b0 ("Narrow remembered
count()/sizeof()of an array when the array expression is narrowed", #6953),mergeWith()drops a remembered call whose merged type equals what the call resolves to on its own. The goal is to let later narrowing of an argument reach the call:count($arr)after$arr !== []. But dropping the entry is not neutral for later merges:if (isMulti()) {}, the scope no longer remembersisMulti(): bool. Inif (!isMulti() || can('update')), the left operand's scope then has no entry, so the||merge leaves a Maybe holder forisMulti()in the truthy branch. A Maybe holder derives no conditional guard, so the merge after theifloses "ifisMulti()is false,$updateDatais defined".ifwithoutelse(Dolibarr): the same Maybe holder (non-falsy-stringfrom the right operand ofgetS() && …) is intersected as if it held the call's type when the conditional expression "if$clientis null,getS()is''|'0'" fires. That makes theelsebranchnever, andgetS()ends upnon-falsy-stringafter theif.Only an argument that narrowing can change can make a remembered call stale. A call without one now keeps its entry, as it did in 2.3.5. This covers calls with no arguments and calls whose arguments are all literals: scalars other than interpolated strings, constants, and class constants of a named class.
count($arr),strlen($s)and the rest ofbug-13750.phpare still dropped as before.The
turbo-extmirror inMutatingScope.cppgets the same check, andgenerated/MutatingScope.his regenerated with PHP 8.5.Not covered: the same pattern on a call with a narrowable argument, such as
if (f($i)) {}followed byif (!f($i) || …), still reports "might not be defined". It behaves the same way as on 2.3.x-dev today, and keeping that entry would undo #6953 for that call.Verification:
tests/PHPStan/Analyser/nsrt/bug-15727.phpandDefinedVariableRuleTest::testBug15727()fail without the fix (4 failing assertions plus the rule error on line 30) and pass with it. They also pass with the extension loaded and only the C++ change in place.make phpstanreports no errors with and without the extension. The strict extension build,smoke.php,signature-parity.phpandside-by-side.phppass.make lint-turbo, because clang-tidy 21 is not available here.