vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.55k stars 659 forks source link

opcache.jit=1205 causes an assertion #9350

Closed edsrzf closed 1 year ago

edsrzf commented 1 year ago

After updating from 5.6.0 to 5.7.1, I'm seeing an assertion when type-checking my code. It happens often, but not 100% of the time.

If I change opcache.jit from 1205 to 1254 (which is the default), the assertion goes away.

System Information

OS: MacOS 13.2.1 CPU: arm64 PHP: 8.1.15

Stack Trace

Uncaught AssertionError: assert(false) in vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php:189
#0 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php(181): assert(false, 'assert(count($r...')
#1 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php(139): Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\ConcatAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Scalar\MagicConst\Dir), Object(PhpParser\Node\Scalar\String_), Object(Psalm\Context), Object(Psalm\Type\Union))
#2 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(266): Psalm\Internal\Analyzer\Statements\Expression\BinaryOpAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\BinaryOp\Concat), Object(Psalm\Context), 0, false)
#3 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\BinaryOp\Concat), Object(Psalm\Context), false, NULL, false, NULL, false)
#4 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php(241): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\BinaryOp\Concat), Object(Psalm\Context), false, NULL, false, NULL)
#5 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php(186): Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Array, Array, 'realpath', true, Object(Psalm\Context), Object(Psalm\Internal\Type\TemplateResult))
#6 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(296): Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context), Object(Psalm\Internal\Type\TemplateResult))
#7 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context), false, NULL, false, NULL, false)
#8 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(242): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context))
#9 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(544): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\PropertyFetch), Object(PhpParser\Node\Expr\FuncCall), NULL, Object(Psalm\Context), NULL, Array, NULL)
#10 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(178): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyzeAssignment(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), true)
#11 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, NULL, true, NULL, false)
#12 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(566): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, NULL, true)
#13 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(195): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), NULL)
#14 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(468): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), NULL, true)
#15 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php(131): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), NULL, true)
#16 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(414): Psalm\Internal\Analyzer\ClassLikeAnalyzer->getMethodMutations('__construct', Object(Psalm\Context))
#17 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1393): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), true)
#18 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(381): Psalm\Internal\Analyzer\ProjectAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), '...', 'apps/backend/co...')
#19 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php(130): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context))
#20 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(882): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\ExistingAtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Internal\MethodIdentifier), 'ProjectConfigur...', Object(Psalm\Storage\ClassLikeStorage), false, NULL)
#21 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(207): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'ProjectConfigur...', false, true, NULL)
#22 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(208): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, true, NULL)
#23 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(190): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), NULL)
#24 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, true, NULL, false)
#25 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(566): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, true)
#26 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(195): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), NULL)
#27 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(468): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), NULL, true)
#28 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php(131): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), NULL, true)
#29 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(414): Psalm\Internal\Analyzer\ClassLikeAnalyzer->getMethodMutations('__construct', Object(Psalm\Context))
#30 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1393): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), true)
#31 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(381): Psalm\Internal\Analyzer\ProjectAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), '...', 'apps/backend/co...')
#32 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php(130): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context))
#33 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(882): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\ExistingAtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Internal\MethodIdentifier), 'sfApplicationCo...', Object(Psalm\Storage\ClassLikeStorage), false, NULL)
#34 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(207): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'sfApplicationCo...', false, true, NULL)
#35 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(208): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, true, NULL)
#36 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(190): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), NULL)
#37 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, true, NULL, false)
#38 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(566): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, true)
#39 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(195): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), NULL)
#40 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(468): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), NULL, true)
#41 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php(131): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), NULL, true)
#42 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(414): Psalm\Internal\Analyzer\ClassLikeAnalyzer->getMethodMutations('__construct', Object(Psalm\Context))
#43 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1393): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), true)
#44 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(381): Psalm\Internal\Analyzer\ProjectAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context), '...', 'apps/backend/co...')
#45 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php(130): Psalm\Internal\Analyzer\FileAnalyzer->getMethodMutations(Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Context))
#46 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(882): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\ExistingAtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Node\VirtualIdentifier), Array, Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Internal\MethodIdentifier), 'VendApplication...', Object(Psalm\Storage\ClassLikeStorage), false, NULL)
#47 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(207): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Node\VirtualIdentifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'VendApplication...', false, true, NULL)
#48 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(208): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, true, NULL)
#49 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(190): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Context), NULL)
#50 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(86): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Context), false, Object(Psalm\Context), true, NULL, false)
#51 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(566): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Expr\VirtualStaticCall), Object(Psalm\Context), false, Object(Psalm\Context), true)
#52 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(195): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Node\Stmt\VirtualExpression), Object(Psalm\Context), Object(Psalm\Context))
#53 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(468): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#54 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1799): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#55 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1226): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(Psalm\Node\Stmt\VirtualClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context), true)
#56 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(551): Psalm\Internal\Analyzer\ClassAnalyzer->checkPropertyInitialization(Object(Psalm\Codebase), Object(Psalm\Config), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Context), Object(Psalm\Context), NULL)
#57 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(203): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#58 vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(1581): Psalm\Internal\Analyzer\FileAnalyzer->analyze()
#59 vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(522): Psalm\Internal\Codebase\Analyzer->analysisWorker(0, '...')
#60 vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(272): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#61 vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1187): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, false)
#62 vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php(374): Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths(Array)
#63 vendor/vimeo/psalm/psalm(9): Psalm\Internal\Cli\Psalm::run(Array)
#64 vendor/bin/psalm(120): include('...')
#65 {main}

Code

The code that triggers this assertion is:

<?php

$v = __DIR__ . '';
// Also works:
// $v = __FILE__ . '';

(This can't be reproduced with a snippet because the snippet likely runs on a different platform.)

Further Debugging

This is probably a JIT bug in PHP. If I add some debug logging I see some truly unexplainable behavior:

Original code:

                    if ($literal_concat) {
                        assert(count($result_type_parts) === $combinations);
                        assert(count($result_type_parts) !== 0); // #8163
                        $result_type = new Union($result_type_parts);
                    }

With added debugging:

                    if ($literal_concat) {
                        if (count($result_type_parts) !== $combinations) {
                            var_dump(count($result_type_parts) === $combinations);
                            var_dump(count($result_type_parts));
                            var_dump($combinations);
                            assert(false);
                        }
                        assert(count($result_type_parts) === $combinations);
                        assert(count($result_type_parts) !== 0); // #8163
                        $result_type = new Union($result_type_parts);
                    }

When the assertion triggers, the output of this code is:

bool(false)
int(1)
int(1)

So, it's saying 1 !== 1, which is...puzzling, to say the least. Also, if I change the order of the logging and move var_dump($combinations) first, the output becomes:

int(1)
bool(true)
int(1)
psalm-github-bot[bot] commented 1 year ago

Hey @edsrzf, can you reproduce the issue on https://psalm.dev ?

simitter commented 1 year ago

Can confirm this problem m1 pro. On older intel macs the problem does not seem to appear.

danog commented 1 year ago

Yep, this is an issue with the ARM64 JIT, can reproduce on my pi4 by scanning psalm itself

danog commented 1 year ago

I'm afraid switching to tracing JIT will not fix the issue, only postpone it until the JIT decides to compile that code segment when it gets hot enough.

The only workaround I see is running (function (int $_): void {})($combinations); before the assertions, or turning them into explicit ifs.

edsrzf commented 1 year ago

Anecdotally, I'm able to type-check a large code base using opcache.jit=1205. I don't know if there's a way to tell whether the code segment is getting hot enough to be JIT compiled.

weirdan commented 1 year ago

~Is this reported to / fixed in https://github.com/php/php-src?~

Yes it is: https://github.com/php/php-src/issues/10635

weirdan commented 1 year ago

The fix is released in 5.7.2.