Repository: numba/numba

Issue:
Lowering error with Numba 0.49.0
## Problem Description

The following code generates a `LoweringError` with Numba 0.49.0 and Numpy 1.18.3.

```python
from numba import njit
import numpy as np


@njit
def f(x):
    arr = np.ones(x)

    for _ in range(2):
        val =  arr * arr
        arr = arr.copy()
        
        
x = 5
f(x)  # <- This line generates the error
```

No errors occur when Numba 0.48.0 is used instead.

## Expected behavior

The above code should in principle give the same result as the following code, which works with the same versions of Numba and NumPy: 

```python
@njit
def g(x):
    arr = np.ones(x)

    for _ in range(2):
        val =  arr ** 2
        arr = arr.copy()
        

g(x)
```

## Traceback

<details>

```python-traceback
--------------------------------------------------------------------
AssertionError                     Traceback (most recent call last)
/anaconda3/lib/python3.7/site-packages/numba/core/errors.py in new_error_context(fmt_, *args, **kwargs)
    800     try:
--> 801         yield
    802     except NumbaError as e:

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_block(self, block)
    266                                    loc=self.loc, errcls_=defaulterrcls):
--> 267                 self.lower_inst(inst)
    268         self.post_block(block)

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_inst(self, inst)
    363             ty = self.typeof(inst.target.name)
--> 364             val = self.lower_assign(ty, inst)
    365             self.storevar(val, inst.target.name)

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_assign(self, ty, inst)
    537         elif isinstance(value, ir.Expr):
--> 538             return self.lower_expr(ty, value)
    539 

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_expr(self, resty, expr)
   1257         elif expr.op in self.context.special_ops:
-> 1258             res = self.context.special_ops[expr.op](self, expr)
   1259             return res

/anaconda3/lib/python3.7/site-packages/numba/np/ufunc/array_exprs.py in _lower_array_expr(lowerer, expr)
    340 
--> 341     with _legalize_parameter_names(expr_var_unique) as expr_params:
    342 

/anaconda3/lib/python3.7/contextlib.py in __enter__(self)
    111         try:
--> 112             return next(self.gen)
    113         except StopIteration:

/anaconda3/lib/python3.7/site-packages/numba/np/ufunc/array_exprs.py in _legalize_parameter_names(var_list)
    312         # Caller should ensure the names are unique
--> 313         assert new_name not in var_map
    314         var_map[new_name] = var, old_name

AssertionError: 

During handling of the above exception, another exception occurred:

LoweringError                      Traceback (most recent call last)
<ipython-input-5-10e1bfec8407> in <module>
     13 
     14 x = 5
---> 15 f(x)

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    418                     e.patch_message('\n'.join((str(e).rstrip(), help_msg)))
    419             # ignore the FULL_TRACEBACKS config, this needs reporting!
--> 420             raise e
    421 
    422     def inspect_llvm(self, signature=None):

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    351                 argtypes.append(self.typeof_pyval(a))
    352         try:
--> 353             return self.compile(tuple(argtypes))
    354         except errors.ForceLiteralArg as e:
    355             # Received request for compiler re-entry with the list of arguments

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     30         def _acquire_compile_lock(*args, **kwargs):
     31             with self:
---> 32                 return func(*args, **kwargs)
     33         return _acquire_compile_lock
     34 

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in compile(self, sig)
    792             self._cache_misses[sig] += 1
    793             try:
--> 794                 cres = self._compiler.compile(args, return_type)
    795             except errors.ForceLiteralArg as e:
    796                 def folded(args, kws):

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in compile(self, args, return_type)
     75 
     76     def compile(self, args, return_type):
---> 77         status, retval = self._compile_cached(args, return_type)
     78         if status:
     79             return retval

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in _compile_cached(self, args, return_type)
     89 
     90         try:
---> 91             retval = self._compile_core(args, return_type)
     92         except errors.TypingError as e:
     93             self._failed_cache[key] = e

/anaconda3/lib/python3.7/site-packages/numba/core/dispatcher.py in _compile_core(self, args, return_type)
    107                                       args=args, return_type=return_type,
    108                                       flags=flags, locals=self.locals,
--> 109                                       pipeline_class=self.pipeline_class)
    110         # Check typing error if object mode is used
    111         if cres.typing_error is not None and not flags.enable_pyobject:

/anaconda3/lib/python3.7/site-packages/numba/core/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
    566     pipeline = pipeline_class(typingctx, targetctx, library,
    567                               args, return_type, flags, locals)
--> 568     return pipeline.compile_extra(func)
    569 
    570 

/anaconda3/lib/python3.7/site-packages/numba/core/compiler.py in compile_extra(self, func)
    337         self.state.lifted = ()
    338         self.state.lifted_from = None
--> 339         return self._compile_bytecode()
    340 
    341     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

/anaconda3/lib/python3.7/site-packages/numba/core/compiler.py in _compile_bytecode(self)
    399         """
    400         assert self.state.func_ir is None
--> 401         return self._compile_core()
    402 
    403     def _compile_ir(self):

/anaconda3/lib/python3.7/site-packages/numba/core/compiler.py in _compile_core(self)
    379                 self.state.status.fail_reason = e
    380                 if is_final_pipeline:
--> 381                     raise e
    382         else:
    383             raise CompilerError("All available pipelines exhausted")

/anaconda3/lib/python3.7/site-packages/numba/core/compiler.py in _compile_core(self)
    370             res = None
    371             try:
--> 372                 pm.run(self.state)
    373                 if self.state.cr is not None:
    374                     break

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_machinery.py in run(self, state)
    339                     (self.pipeline_name, pass_desc)
    340                 patched_exception = self._patch_error(msg, e)
--> 341                 raise patched_exception
    342 
    343     def dependency_analysis(self):

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_machinery.py in run(self, state)
    330                 pass_inst = _pass_registry.get(pss).pass_inst
    331                 if isinstance(pass_inst, CompilerPass):
--> 332                     self._runPass(idx, pass_inst, state)
    333                 else:
    334                     raise BaseException("Legacy pass in use")

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     30         def _acquire_compile_lock(*args, **kwargs):
     31             with self:
---> 32                 return func(*args, **kwargs)
     33         return _acquire_compile_lock
     34 

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    289             mutated |= check(pss.run_initialization, internal_state)
    290         with SimpleTimer() as pass_time:
--> 291             mutated |= check(pss.run_pass, internal_state)
    292         with SimpleTimer() as finalize_time:
    293             mutated |= check(pss.run_finalizer, internal_state)

/anaconda3/lib/python3.7/site-packages/numba/core/compiler_machinery.py in check(func, compiler_state)
    262 
    263         def check(func, compiler_state):
--> 264             mangled = func(compiler_state)
    265             if mangled not in (True, False):
    266                 msg = ("CompilerPass implementations should return True/False. "

/anaconda3/lib/python3.7/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    440 
    441         # TODO: Pull this out into the pipeline
--> 442         NativeLowering().run_pass(state)
    443         lowered = state['cr']
    444         signature = typing.signature(state.return_type, *state.args)

/anaconda3/lib/python3.7/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    368                 lower = lowering.Lower(targetctx, library, fndesc, interp,
    369                                        metadata=metadata)
--> 370                 lower.lower()
    371                 if not flags.no_cpython_wrapper:
    372                     lower.create_cpython_wrapper(flags.release_gil)

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower(self)
    177         if self.generator_info is None:
    178             self.genlower = None
--> 179             self.lower_normal_function(self.fndesc)
    180         else:
    181             self.genlower = self.GeneratorLower(self)

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_normal_function(self, fndesc)
    225         # Init argument values
    226         self.extract_function_arguments()
--> 227         entry_block_tail = self.lower_function_body()
    228 
    229         # Close tail of entry block

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_function_body(self)
    251             bb = self.blkmap[offset]
    252             self.builder.position_at_end(bb)
--> 253             self.lower_block(block)
    254         self.post_lower()
    255         return entry_block_tail

/anaconda3/lib/python3.7/site-packages/numba/core/lowering.py in lower_block(self, block)
    265             with new_error_context('lowering "{inst}" at {loc}', inst=inst,
    266                                    loc=self.loc, errcls_=defaulterrcls):
--> 267                 self.lower_inst(inst)
    268         self.post_block(block)
    269 

/anaconda3/lib/python3.7/contextlib.py in __exit__(self, type, value, traceback)
    128                 value = type()
    129             try:
--> 130                 self.gen.throw(type, value, traceback)
    131             except StopIteration as exc:
    132                 # Suppress StopIteration *unless* it's the same exception that

/anaconda3/lib/python3.7/site-packages/numba/core/errors.py in new_error_context(fmt_, *args, **kwargs)
    806         newerr = errcls(e).add_context(_format_msg(fmt_, args, kwargs))
    807         tb = sys.exc_info()[2] if numba.core.config.FULL_TRACEBACKS else None
--> 808         reraise(type(newerr), newerr, tb)
    809 
    810 

/anaconda3/lib/python3.7/site-packages/numba/core/utils.py in reraise(tp, value, tb)
     78     if value.__traceback__ is not tb:
     79         raise value.with_traceback(tb)
---> 80     raise value
     81 
     82 

LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)


File "<ipython-input-5-10e1bfec8407>", line 10:
def f(x):
    <source elided>
    for _ in range(2):
        val =  arr * arr
        ^

[1] During: lowering "$30binary_multiply.4 = arrayexpr(expr=(<built-in function mul>, [Var(arr_2, <ipython-input-5-10e1bfec8407>:9), Var(arr.2, <ipython-input-5-10e1bfec8407>:9)]), ty=array(float64, 1d, C))" at <ipython-input-5-10e1bfec8407> (10)

```

</details>




Discussion:
Confirmed regression from 0.48.0
likely SSA related

Resolving patch:
diff --git a/numba/np/ufunc/array_exprs.py b/numba/np/ufunc/array_exprs.py
--- a/numba/np/ufunc/array_exprs.py
+++ b/numba/np/ufunc/array_exprs.py
@@ -308,9 +308,11 @@ def _legalize_parameter_names(var_list):
     var_map = OrderedDict()
     for var in var_list:
         old_name = var.name
-        new_name = old_name.replace("$", "_").replace(".", "_")
+        new_name = var.scope.redefine(old_name, loc=var.loc).name
+        new_name = new_name.replace("$", "_").replace(".", "_")
         # Caller should ensure the names are unique
-        assert new_name not in var_map
+        if new_name in var_map:
+            raise AssertionError(f"{new_name!r} not unique")
         var_map[new_name] = var, old_name
         var.name = new_name
     param_names = list(var_map)
@@ -337,17 +339,9 @@ def _lower_array_expr(lowerer, expr):
     expr_args = [var.name for var in expr_var_unique]
 
     # 1. Create an AST tree from the array expression.
-
     with _legalize_parameter_names(expr_var_unique) as expr_params:
-
-        if hasattr(ast, "arg"):
-            # Should be Python 3.x
-            ast_args = [ast.arg(param_name, None)
-                        for param_name in expr_params]
-        else:
-            # Should be Python 2.x
-            ast_args = [ast.Name(param_name, ast.Param())
-                        for param_name in expr_params]
+        ast_args = [ast.arg(param_name, None)
+                    for param_name in expr_params]
         # Parse a stub function to ensure the AST is populated with
         # reasonable defaults for the Python version.
         ast_module = ast.parse('def {0}(): return'.format(expr_name),


Test patch:
diff --git a/numba/tests/test_array_exprs.py b/numba/tests/test_array_exprs.py
--- a/numba/tests/test_array_exprs.py
+++ b/numba/tests/test_array_exprs.py
@@ -467,6 +467,21 @@ def test_annotations(self):
         self.assertIn("#   u.1 = ", res)
         self.assertIn("#   u.2 = ", res)
 
+    def test_issue_5599_name_collision(self):
+        # The original error will fail in lowering of the array_expr
+        @njit
+        def f(x):
+            arr = np.ones(x)
+
+            for _ in range(2):
+                val =  arr * arr
+                arr = arr.copy()
+            return arr
+
+        got = f(5)
+        expect = f.py_func(5)
+        np.testing.assert_array_equal(got, expect)
+
 
 class TestSemantics(MemoryLeakMixin, unittest.TestCase):