php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80184 Complex expression in while / if statements resolves to false incorrectly
Submitted: 2020-10-04 12:58 UTC Modified: 2020-10-05 09:17 UTC
From: jelly dot legend at gmail dot com Assigned:
Status: Closed Package: opcache
PHP Version: 8.0.0rc1 OS: MacOS 10.15.6
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: jelly dot legend at gmail dot com
New email:
PHP Version: OS:

 

 [2020-10-04 12:58 UTC] jelly dot legend at gmail dot com
Description:
------------
If a use a complex expression as an if condition or while condition, it incorrectly resolves to false. While it correctly evaluates it if passed to a variable and printed.

PHP version:
PHP 8.0.0rc1 (cli) (built: Oct  4 2020 13:58:56) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.0rc1, Copyright (c), by Zend Technologies


php.development-ini changes:
zend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128


If I disable opcache I get the correct results.
PHP was built from source. With following configuration.

./configure \
  --prefix=.../php-8.0.0rc1 \
  --with-config-file-path=.../php-8.0.0rc1/etc \
  --enable-mbstring \
  --enable-sockets \
  --enable-opcache \
  --with-openssl=/usr/local/openssl \
  --with-curl \
  --with-sodium \
  --with-bz2 \
  --with-zlib \
  --with-zip

I was first noticed the bug on beta-4 but decided to try it out in rc-1 instead.


Test script:
---------------
$factory = function (string $msg): Closure {
    return function () use ($msg): void {
        echo $msg;
    };
};

$callbacks = [
    $factory('First item!'),
    $factory('Second item!'),
    $factory('Third item!'),
    $factory('Fourth item!'),
];

$nl = function (): void {
    echo PHP_EOL;
};


while ((null !== $callback = array_shift($callbacks)) && ($callback() || $nl() || true))
    /**/; // exists after first item, should iterate thought all


// Full test at: https://pastebin.com/br54nGvM

Expected result:
----------------
First item!
Second item!
Third item!
Fourth item!

Actual result:
--------------
First item!

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-10-05 09:08 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2020-10-05 09:08 UTC] nikic@php.net
Also happens with opcache.jit=0, so generic optimization issue.

Slightly reduced by Dharman:

<?php
  
$callbacks = [
    function () { echo "First item!\n"; },
    function () { echo "Second item!\n"; },
    function () { echo "Third item!\n"; },
    function () { echo "Fourth item!\n"; },
];

while ($callback = array_shift($callbacks) and $callback() || true); 

This iterates once instead of multiple times.
 [2020-10-05 09:17 UTC] nikic@php.net
Culprit is the block pass:

$_main:
     ; (lines=22, args=0, vars=2, tmps=10)
     ; (before block pass)
     ; /home/nikic/php/php-src/t170.php:1-11
     ; return  [] RANGE[0..0]
BB0:
     ; start lines=[0-9]
     ; to=(BB1)
0000 T2 = DECLARE_LAMBDA_FUNCTION string("")
0001 T3 = INIT_ARRAY 4 (packed) T2 NEXT
0002 T4 = DECLARE_LAMBDA_FUNCTION string("")
0003 T3 = ADD_ARRAY_ELEMENT T4 NEXT
0004 T5 = DECLARE_LAMBDA_FUNCTION string("")
0005 T3 = ADD_ARRAY_ELEMENT T5 NEXT
0006 T6 = DECLARE_LAMBDA_FUNCTION string("")
0007 T3 = ADD_ARRAY_ELEMENT T6 NEXT
0008 ASSIGN CV0($callbacks) T3
0009 NOP

BB1:
     ; follow target lines=[10-14]
     ; to=(BB4, BB2)
0010 INIT_FCALL 1 96 string("array_shift")
0011 SEND_REF CV0($callbacks) 1
0012 V8 = DO_ICALL
0013 T9 = ASSIGN CV1($callback) V8
0014 T9 = JMPZ_EX T9 BB4

BB2:
     ; follow lines=[15-17]
     ; to=(BB1, BB3)
0015 INIT_DYNAMIC_CALL 0 CV1($callback)
0016 V10 = DO_FCALL
0017 T9 = JMPNZ_EX V10 BB1

BB3:
     ; follow lines=[18-20]
     ; to=(BB1, BB4)
0018 T11 = BOOL bool(true)
0019 T9 = BOOL T11
0020 JMPNZ T9 BB1

BB4:
     ; follow target exit lines=[21-21]
0021 RETURN int(1)

$_main:
     ; (lines=17, args=0, vars=2, tmps=10)
     ; (after block pass)
     ; /home/nikic/php/php-src/t170.php:1-11
     ; return  [] RANGE[0..0]
BB0:
     ; start lines=[0-13]
     ; to=(BB4, BB2)
0000 T2 = DECLARE_LAMBDA_FUNCTION string("")
0001 T3 = INIT_ARRAY 4 (packed) T2 NEXT
0002 T4 = DECLARE_LAMBDA_FUNCTION string("")
0003 T3 = ADD_ARRAY_ELEMENT T4 NEXT
0004 T5 = DECLARE_LAMBDA_FUNCTION string("")
0005 T3 = ADD_ARRAY_ELEMENT T5 NEXT
0006 T6 = DECLARE_LAMBDA_FUNCTION string("")
0007 T3 = ADD_ARRAY_ELEMENT T6 NEXT
0008 ASSIGN CV0($callbacks) T3
0009 INIT_FCALL 1 96 string("array_shift")
0010 SEND_REF CV0($callbacks) 1
0011 V8 = DO_ICALL
0012 T9 = ASSIGN CV1($callback) V8
0013 JMPZ T9 BB4

BB1:
     ; follow exit empty

BB2:
     ; follow lines=[14-15]
     ; to=(BB1)
0014 INIT_DYNAMIC_CALL 0 CV1($callback)
0015 DO_FCALL

BB4:
     ; target exit lines=[16-16]
0016 RETURN int(1)

These are clearly not the same.
 [2020-10-05 13:24 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6ea870f5fb94c251cf201fb2955a93ba70edaa89
Log: Fix bug #80184
 [2020-10-05 13:24 UTC] nikic@php.net
-Status: Verified +Status: Closed
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Sat Feb 01 23:01:29 2025 UTC