Repository: numba/numba

Issue:
Jit produces wrong code with version 0.49.0
[Our library's](https://github.com/pymatting/pymatting/issues/15) tests [fail on all platforms](https://travis-ci.org/github/pymatting/pymatting/builds/678580669) when upgrading from numba version `0.48.0` to `0.49.0`.

The smallest test case that I could come up with that still reproduces the issue is the following:

```Python
import numpy as np
from numba import jit

def foo():
    values = np.zeros(1)
    tmp = 666
    if tmp:
        values[0] = tmp
        print(values)

print("Without jit")
foo()
print("With jit")
jit(foo)()
```

It prints `666.0` with Python and `1.0` when jitted.

- [ x ] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [ x ] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).



Discussion:
I can add the following observations (with slightly more simplified code (no numpy))

```python
from numba import njit

@njit
def foo1():
    res = 0
    tmp = 10
    if tmp:
        res = tmp
    return res

print(foo1())
#Returns 1

@njit
def foo2():
    res = 0
    tmp = 10
    if tmp != 0:
        res = tmp
    return res

print(foo2())
#Returns 10

@njit
def foo3(tmp):
    res = 0
    if tmp:
        res = tmp
    return res

print(foo3(10))
#Returns 10
```
So there is definitely an issue that only arises when using a constant value and skipping the explicit comparison in the if statement. I do not know whether that is the root cause of the original problem in @99991 's project though.
Thanks for the report. This is a bug.
Will patch it for 0.49.1.
This is the first time I look into the numba src, but it looks like the conditions are added to the list of nullified conditions after pruning 
https://github.com/numba/numba/blob/334e65cef19257faf1c6452bc0750df7d1ce76d8/numba/core/analysis.py#L443-L447
and subsequently replaced by 0 or 1?
https://github.com/numba/numba/blob/334e65cef19257faf1c6452bc0750df7d1ce76d8/numba/core/analysis.py#L464-L475
@HPLegion correct, this is trying to reuse the rewrite that was written for the case of conditionals. I've got a patch to fix it.

Resolving patch:
diff --git a/numba/core/analysis.py b/numba/core/analysis.py
--- a/numba/core/analysis.py
+++ b/numba/core/analysis.py
@@ -283,6 +283,10 @@ def _fix_loop_exit(cfg, loop):
         return loop
 
 
+# Used to describe a nullified condition in dead branch pruning
+nullified = namedtuple('nullified', 'condition, taken_br, rewrite_stmt')
+
+
 # Functions to manipulate IR
 def dead_branch_prune(func_ir, called_args):
     """
@@ -429,7 +433,8 @@ def resolve_input_arg_const(input_arg_idx):
                 prune_stat, taken = prune(branch, condition, blk, *const_conds)
                 if(prune_stat):
                     # add the condition to the list of nullified conditions
-                    nullified_conditions.append((condition, taken))
+                    nullified_conditions.append(nullified(condition, taken,
+                                                          True))
         else:
             # see if this is a branch on a constant value predicate
             resolved_const = Unknown()
@@ -444,7 +449,8 @@ def resolve_input_arg_const(input_arg_idx):
                 prune_stat, taken = prune_by_predicate(branch, condition, blk)
                 if(prune_stat):
                     # add the condition to the list of nullified conditions
-                    nullified_conditions.append((condition, taken))
+                    nullified_conditions.append(nullified(condition, taken,
+                                                          False))
 
     # 'ERE BE DRAGONS...
     # It is the evaluation of the condition expression that often trips up type
@@ -461,18 +467,22 @@ def resolve_input_arg_const(input_arg_idx):
     # completeness the func_ir._definitions and ._consts are also updated to
     # make the IR state self consistent.
 
-    deadcond = [x[0] for x in nullified_conditions]
+    deadcond = [x.condition for x in nullified_conditions]
     for _, cond, blk in branch_info:
         if cond in deadcond:
             for x in blk.body:
                 if isinstance(x, ir.Assign) and x.value is cond:
                     # rewrite the condition as a true/false bit
-                    branch_bit = nullified_conditions[deadcond.index(cond)][1]
-                    x.value = ir.Const(branch_bit, loc=x.loc)
-                    # update the specific definition to the new const
-                    defns = func_ir._definitions[x.target.name]
-                    repl_idx = defns.index(cond)
-                    defns[repl_idx] = x.value
+                    nullified_info = nullified_conditions[deadcond.index(cond)]
+                    # only do a rewrite of conditions, predicates need to retain
+                    # their value as they may be used later.
+                    if nullified_info.rewrite_stmt:
+                        branch_bit = nullified_info.taken_br
+                        x.value = ir.Const(branch_bit, loc=x.loc)
+                        # update the specific definition to the new const
+                        defns = func_ir._definitions[x.target.name]
+                        repl_idx = defns.index(cond)
+                        defns[repl_idx] = x.value
 
     # Remove dead blocks, this is safe as it relies on the CFG only.
     cfg = compute_cfg_from_blocks(func_ir.blocks)


Test patch:
diff --git a/numba/tests/test_analysis.py b/numba/tests/test_analysis.py
--- a/numba/tests/test_analysis.py
+++ b/numba/tests/test_analysis.py
@@ -697,7 +697,7 @@ def test_single_if_freevar(self):
 
                 def func(x):
                     if const:
-                        return 3.14159
+                        return 3.14159, const
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
@@ -707,7 +707,7 @@ def test_single_if_negate_freevar(self):
 
                 def func(x):
                     if not const:
-                        return 3.14159
+                        return 3.14159, const
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
@@ -717,9 +717,9 @@ def test_single_if_else_freevar(self):
 
                 def func(x):
                     if const:
-                        return 3.14159
+                        return 3.14159, const
                     else:
-                        return 1.61803
+                        return 1.61803, const
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
@@ -729,9 +729,9 @@ def test_single_if_else_negate_freevar(self):
 
                 def func(x):
                     if not const:
-                        return 3.14159
+                        return 3.14159, const
                     else:
-                        return 1.61803
+                        return 1.61803, const
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
@@ -746,7 +746,7 @@ def test_single_if_global(self):
 
                 def func(x):
                     if c_test_single_if_global:
-                        return 3.14159
+                        return 3.14159, c_test_single_if_global
 
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
@@ -760,7 +760,7 @@ def test_single_if_negate_global(self):
 
                 def func(x):
                     if c_test_single_if_negate_global:
-                        return 3.14159
+                        return 3.14159, c_test_single_if_negate_global
 
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
@@ -774,9 +774,9 @@ def test_single_if_else_global(self):
 
                 def func(x):
                     if c_test_single_if_else_global:
-                        return 3.14159
+                        return 3.14159, c_test_single_if_else_global
                     else:
-                        return 1.61803
+                        return 1.61803, c_test_single_if_else_global
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
@@ -789,12 +789,25 @@ def test_single_if_else_negate_global(self):
 
                 def func(x):
                     if not c_test_single_if_else_negate_global:
-                        return 3.14159
+                        return 3.14159, c_test_single_if_else_negate_global
                     else:
-                        return 1.61803
+                        return 1.61803, c_test_single_if_else_negate_global
                 self.assert_prune(func, (types.NoneType('none'),), [prune],
                                   None)
 
+    def test_issue_5618(self):
+
+        @njit
+        def foo():
+            values = np.zeros(1)
+            tmp = 666
+            if tmp:
+                values[0] = tmp
+            return values
+
+        self.assertPreciseEqual(foo.py_func()[0], 666.)
+        self.assertPreciseEqual(foo()[0], 666.)
+
 
 class TestBranchPrunePostSemanticConstRewrites(TestBranchPruneBase):
     # Tests that semantic constants rewriting works by virtue of branch pruning