Repository: numba/numba

Issue:
Parallel prange gives LoweringError
Hello, and thank you for supporting such a great package. I have had a lot of success implementing parallel computations with Numba, although it is often difficult to debug. I have run into a problem that has me stuck. The purpose of the function is to perform several sum reductions. My code is able to compile in nopython mode, but fails when I enable parallel. It is kind of a delicate error, and further simplification of my MWE leads to a successful parallel compile. I use the environment
- macOS 10.14.6
- Python 3.7.5
- Numba 0.46.0
- NumPy 1.17.4

The code example is
```python
import numpy as np
import numba as nb


def foo(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k):
    n, _, dz = zz.shape
    _, _, dx = xx.shape
    _, _, dv = vv.shape

    lg = 0.0
    lh = np.zeros(dx)
    lc = np.zeros(dv)
    
    for i in nb.prange(n):
        nt = tc[i]
        
        if k == 'a' or k == 'b' or k == 'c' or k == 'all':
            for t in range(nt):
                if yyhi[i, t]:
                    lg += 1.0
        
        if k == 'd' or k == 'e' or k == 'all':
            for t in range(nt):
                swz = s + w @ zz[i, t]
                lh += swz * yyh[i, t] - np.log1p(np.exp(swz))
        
        if k == 'f' or k == 'g' or k == 'all':
            for t in range(nt):
                if vvi[i, t]:
                    lc += np.exp(h @ zz[i, t])
    
    return 'done'


n = 2
dz = 2
dx = 2
dv = 2

zz = np.ones((n, n, dz))
xx = np.ones((n, n, dx))
vv = np.ones((n, n, dv))
yyh = ~np.isnan(xx)
yyhi = np.any(yyh, axis=2)
vvi = np.any(~np.isnan(vv), axis=2)
tc = n * np.ones(n, dtype=np.int_)
s = np.zeros(dx)
w = np.zeros((dx, dz))
h = np.zeros((dv, dz))
k = 'all'

# nb.njit()(foo)(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k)  # This compiles and runs
nb.njit(parallel=True)(foo)(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k) # This does not compile
```
If I run the first (commented-out) njit foo, I get the expected output
```
'done'
```
If I run the second njit parallel foo, I get the following
```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/numba/errors.py in new_error_context(fmt_, *args, **kwargs)
    716     try:
--> 717         yield
    718     except NumbaError as e:

~/anaconda3/lib/python3.7/site-packages/numba/lowering.py in lower_block(self, block)
    259                                    loc=self.loc, errcls_=defaulterrcls):
--> 260                 self.lower_inst(inst)
    261 

~/anaconda3/lib/python3.7/site-packages/numba/lowering.py in lower_inst(self, inst)
    413                 if isinstance(inst, _class):
--> 414                     func(self, inst)
    415                     return

~/anaconda3/lib/python3.7/site-packages/numba/npyufunc/parfor.py in _lower_parfor_parallel(lowerer, parfor)
    239         lowerer, parfor, typemap, typingctx, targetctx, flags, {},
--> 240         bool(alias_map), index_var_typ, parfor.races)
    241     numba.parfor.sequential_parfor_lowering = False

~/anaconda3/lib/python3.7/site-packages/numba/npyufunc/parfor.py in _create_gufunc_for_parfor_body(lowerer, parfor, typemap, typingctx, targetctx, flags, locals, has_aliases, index_var_typ, races)
   1124         flags,
-> 1125         locals)
   1126 

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in compile_ir(typingctx, targetctx, func_ir, args, return_type, flags, locals, lifted, lifted_from, is_lifted_loop, library, pipeline_class)
    591         return pipeline.compile_ir(func_ir=func_ir, lifted=lifted,
--> 592                                    lifted_from=lifted_from)
    593 

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in compile_ir(self, func_ir, lifted, lifted_from)
    335         FixupArgs().run_pass(self.state)
--> 336         return self._compile_ir()
    337 

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_ir(self)
    391         assert self.state.func_ir is not None
--> 392         return self._compile_core()
    393 

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_core(self)
    364                 if is_final_pipeline:
--> 365                     raise e
    366         else:

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_core(self)
    355             try:
--> 356                 pm.run(self.state)
    357                 if self.state.cr is not None:

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in run(self, state)
    327                 patched_exception = self._patch_error(msg, e)
--> 328                 raise patched_exception
    329 

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in run(self, state)
    318                 if isinstance(pass_inst, CompilerPass):
--> 319                     self._runPass(idx, pass_inst, state)
    320                 else:

~/anaconda3/lib/python3.7/site-packages/numba/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     31             with self:
---> 32                 return func(*args, **kwargs)
     33         return _acquire_compile_lock

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    280         with SimpleTimer() as pass_time:
--> 281             mutated |= check(pss.run_pass, internal_state)
    282         with SimpleTimer() as finalize_time:

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in check(func, compiler_state)
    267         def check(func, compiler_state):
--> 268             mangled = func(compiler_state)
    269             if mangled not in (True, False):

~/anaconda3/lib/python3.7/site-packages/numba/typed_passes.py in run_pass(self, state)
     93                 state.return_type,
---> 94                 state.locals)
     95             state.typemap = typemap

~/anaconda3/lib/python3.7/site-packages/numba/typed_passes.py in type_inference_stage(typingctx, interp, args, return_type, locals)
     64 
---> 65         infer.build_constraint()
     66         infer.propagate()

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in build_constraint(self)
    903             for inst in blk.body:
--> 904                 self.constrain_statement(inst)
    905 

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in constrain_statement(self, inst)
   1187         if isinstance(inst, ir.Assign):
-> 1188             self.typeof_assign(inst)
   1189         elif isinstance(inst, ir.SetItem):

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in typeof_assign(self, inst)
   1258         elif isinstance(value, (ir.Global, ir.FreeVar)):
-> 1259             self.typeof_global(inst, inst.target, value)
   1260         elif isinstance(value, ir.Arg):

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in typeof_global(self, inst, target, gvar)
   1399         lit = types.maybe_literal(gvar.value)
-> 1400         self.lock_type(target.name, lit or typ, loc=inst.loc)
   1401         self.assumed_immutables.add(inst)

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in lock_type(self, var, tp, loc, literal_value)
    975         tv = self.typevars[var]
--> 976         tv.lock(tp, loc=loc, literal_value=literal_value)
    977 

~/anaconda3/lib/python3.7/site-packages/numba/typeinfer.py in lock(self, tp, loc, literal_value)
     82         assert isinstance(tp, types.Type), type(tp)
---> 83         assert not self.locked
     84 

AssertionError: Failed in nopython mode pipeline (step: nopython frontend)


During handling of the above exception, another exception occurred:

LoweringError                             Traceback (most recent call last)
<ipython-input-1-1ddce8bd8630> in <module>
     51 
     52 # nb.njit()(foo)(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k)
---> 53 nb.njit(parallel=True)(foo)(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k)

~/anaconda3/lib/python3.7/site-packages/numba/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/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/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/dispatcher.py in compile(self, sig)
    766             self._cache_misses[sig] += 1
    767             try:
--> 768                 cres = self._compiler.compile(args, return_type)
    769             except errors.ForceLiteralArg as e:
    770                 def folded(args, kws):

~/anaconda3/lib/python3.7/site-packages/numba/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/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/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/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
    526     pipeline = pipeline_class(typingctx, targetctx, library,
    527                               args, return_type, flags, locals)
--> 528     return pipeline.compile_extra(func)
    529 
    530 

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in compile_extra(self, func)
    324         self.state.lifted = ()
    325         self.state.lifted_from = None
--> 326         return self._compile_bytecode()
    327 
    328     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_bytecode(self)
    383         """
    384         assert self.state.func_ir is None
--> 385         return self._compile_core()
    386 
    387     def _compile_ir(self):

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_core(self)
    363                 self.state.status.fail_reason = e
    364                 if is_final_pipeline:
--> 365                     raise e
    366         else:
    367             raise CompilerError("All available pipelines exhausted")

~/anaconda3/lib/python3.7/site-packages/numba/compiler.py in _compile_core(self)
    354             res = None
    355             try:
--> 356                 pm.run(self.state)
    357                 if self.state.cr is not None:
    358                     break

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in run(self, state)
    326                     (self.pipeline_name, pass_desc)
    327                 patched_exception = self._patch_error(msg, e)
--> 328                 raise patched_exception
    329 
    330     def dependency_analysis(self):

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in run(self, state)
    317                 pass_inst = _pass_registry.get(pss).pass_inst
    318                 if isinstance(pass_inst, CompilerPass):
--> 319                     self._runPass(idx, pass_inst, state)
    320                 else:
    321                     raise BaseException("Legacy pass in use")

~/anaconda3/lib/python3.7/site-packages/numba/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/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    279             mutated |= check(pss.run_initialization, internal_state)
    280         with SimpleTimer() as pass_time:
--> 281             mutated |= check(pss.run_pass, internal_state)
    282         with SimpleTimer() as finalize_time:
    283             mutated |= check(pss.run_finalizer, internal_state)

~/anaconda3/lib/python3.7/site-packages/numba/compiler_machinery.py in check(func, compiler_state)
    266 
    267         def check(func, compiler_state):
--> 268             mangled = func(compiler_state)
    269             if mangled not in (True, False):
    270                 msg = ("CompilerPass implementations should return True/False. "

~/anaconda3/lib/python3.7/site-packages/numba/typed_passes.py in run_pass(self, state)
    378             state.library.enable_object_caching()
    379 
--> 380         NativeLowering().run_pass(state) # TODO: Pull this out into the pipeline
    381         lowered = state['cr']
    382         signature = typing.signature(state.return_type, *state.args)

~/anaconda3/lib/python3.7/site-packages/numba/typed_passes.py in run_pass(self, state)
    323                 lower = lowering.Lower(targetctx, library, fndesc, interp,
    324                                        metadata=metadata)
--> 325                 lower.lower()
    326                 if not flags.no_cpython_wrapper:
    327                     lower.create_cpython_wrapper(flags.release_gil)

~/anaconda3/lib/python3.7/site-packages/numba/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/lowering.py in lower_normal_function(self, fndesc)
    218         # Init argument values
    219         self.extract_function_arguments()
--> 220         entry_block_tail = self.lower_function_body()
    221 
    222         # Close tail of entry block

~/anaconda3/lib/python3.7/site-packages/numba/lowering.py in lower_function_body(self)
    243             bb = self.blkmap[offset]
    244             self.builder.position_at_end(bb)
--> 245             self.lower_block(block)
    246 
    247         self.post_lower()

~/anaconda3/lib/python3.7/site-packages/numba/lowering.py in lower_block(self, block)
    258             with new_error_context('lowering "{inst}" at {loc}', inst=inst,
    259                                    loc=self.loc, errcls_=defaulterrcls):
--> 260                 self.lower_inst(inst)
    261 
    262     def create_cpython_wrapper(self, release_gil=False):

~/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/errors.py in new_error_context(fmt_, *args, **kwargs)
    723         from numba import config
    724         tb = sys.exc_info()[2] if config.FULL_TRACEBACKS else None
--> 725         six.reraise(type(newerr), newerr, tb)
    726 
    727 

~/anaconda3/lib/python3.7/site-packages/numba/six.py in reraise(tp, value, tb)
    667         if value.__traceback__ is not tb:
    668             raise value.with_traceback(tb)
--> 669         raise value
    670 
    671 else:

LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)
Failed in nopython mode pipeline (step: nopython frontend)


File "<ipython-input-1-1ddce8bd8630>", line 14:
def foo(zz, xx, yyh, yyhi, vv, vvi, tc, s, w, h, k):
    <source elided>
    
    for i in nb.prange(n):
    ^

[1] During: lowering "id=5[LoopNest(index_variable = parfor_index.104, range = (0, $n.119, 1))]{256: <ir.Block at <ipython-input-1-1ddce8bd8630> (23)>, 258: <ir.Block at <ipython-input-1-1ddce8bd8630> (25)>, 130: <ir.Block at <ipython-input-1-1ddce8bd8630> (18)>, 132: <ir.Block at <ipython-input-1-1ddce8bd8630> (18)>, 268: <ir.Block at <ipython-input-1-1ddce8bd8630> (25)>, 146: <ir.Block at <ipython-input-1-1ddce8bd8630> (19)>, 278: <ir.Block at <ipython-input-1-1ddce8bd8630> (27)>, 156: <ir.Block at <ipython-input-1-1ddce8bd8630> (18)>, 158: <ir.Block at <ipython-input-1-1ddce8bd8630> (20)>, 286: <ir.Block at <ipython-input-1-1ddce8bd8630> (27)>, 166: <ir.Block at <ipython-input-1-1ddce8bd8630> (20)>, 296: <ir.Block at <ipython-input-1-1ddce8bd8630> (27)>, 298: <ir.Block at <ipython-input-1-1ddce8bd8630> (28)>, 174: <ir.Block at <ipython-input-1-1ddce8bd8630> (22)>, 184: <ir.Block at <ipython-input-1-1ddce8bd8630> (22)>, 314: <ir.Block at <ipython-input-1-1ddce8bd8630> (28)>, 194: <ir.Block at <ipython-input-1-1ddce8bd8630> (22)>, 196: <ir.Block at <ipython-input-1-1ddce8bd8630> (23)>, 78: <ir.Block at <ipython-input-1-1ddce8bd8630> (14)>, 344: <ir.Block at <ipython-input-1-1ddce8bd8630> (29)>, 348: <ir.Block at <ipython-input-1-1ddce8bd8630> (28)>, 351: <ir.Block at <ipython-input-1-1ddce8bd8630> (30)>, 352: <ir.Block at <ipython-input-1-1ddce8bd8630> (14)>, 96: <ir.Block at <ipython-input-1-1ddce8bd8630> (17)>, 104: <ir.Block at <ipython-input-1-1ddce8bd8630> (17)>, 112: <ir.Block at <ipython-input-1-1ddce8bd8630> (17)>, 120: <ir.Block at <ipython-input-1-1ddce8bd8630> (17)>}Var(parfor_index.104, <ipython-input-1-1ddce8bd8630> (14))" at <ipython-input-1-1ddce8bd8630> (14)
```


Discussion:
Thanks for the report. I can reproduce locally, this is likely a bug.
Could this be related to #4846 since the "k" argument of foo() is not a numpy array?
@HPLegion I don't think so. The above is failing because type inference is struggling.

Resolving patch:
diff --git a/numba/parfor.py b/numba/parfor.py
--- a/numba/parfor.py
+++ b/numba/parfor.py
@@ -77,7 +77,8 @@
     is_get_setitem,
     index_var_of_get_setitem,
     set_index_var_of_get_setitem,
-    find_potential_aliases)
+    find_potential_aliases,
+    replace_var_names)
 
 from numba.analysis import (compute_use_defs, compute_live_map,
                             compute_dead_maps, compute_cfg_from_blocks)
@@ -1547,7 +1548,7 @@ def run(self):
         simplify(self.func_ir, self.typemap, self.calltypes)
         # push function call variables inside parfors so gufunc function
         # wouldn't need function variables as argument
-        push_call_vars(self.func_ir.blocks, {}, {})
+        push_call_vars(self.func_ir.blocks, {}, {}, self.typemap)
         # simplify again
         simplify(self.func_ir, self.typemap, self.calltypes)
         dprint_func_ir(self.func_ir, "after optimization")
@@ -2676,7 +2677,7 @@ def _arrayexpr_tree_to_ir(
                 func_var_name = _find_func_var(typemap, op, avail_vars)
                 func_var = ir.Var(scope, mk_unique_var(func_var_name), loc)
                 typemap[func_var.name] = typemap[func_var_name]
-                func_var_def = func_ir.get_definition(func_var_name)
+                func_var_def = copy.deepcopy(func_ir.get_definition(func_var_name))
                 if isinstance(func_var_def, ir.Expr) and func_var_def.op == 'getattr' and func_var_def.attr == 'sqrt':
                      g_math_var = ir.Var(scope, mk_unique_var("$math_g_var"), loc)
                      typemap[g_math_var.name] = types.misc.Module(math)
@@ -3909,7 +3910,7 @@ def apply_copies_parfor(parfor, var_dict, name_var_table,
 ir_utils.apply_copy_propagate_extensions[Parfor] = apply_copies_parfor
 
 
-def push_call_vars(blocks, saved_globals, saved_getattrs, nested=False):
+def push_call_vars(blocks, saved_globals, saved_getattrs, typemap, nested=False):
     """push call variables to right before their call site.
     assuming one global/getattr is created for each call site and control flow
     doesn't change it.
@@ -3919,6 +3920,12 @@ def push_call_vars(blocks, saved_globals, saved_getattrs, nested=False):
         # global/attr variables that are defined in this block already,
         #   no need to reassign them
         block_defs = set()
+        # Some definitions are copied right before the call but then we
+        # need to rename that symbol in that block so that typing won't
+        # generate an error trying to lock the save var twice.
+        # In rename_dict, we collect the symbols that must be renamed in
+        # this block.  We collect them then apply the renaming at the end.
+        rename_dict = {}
         for stmt in block.body:
             def process_assign(stmt):
                 if isinstance(stmt, ir.Assign):
@@ -3937,33 +3944,58 @@ def process_assign(stmt):
                 for s in stmt.init_block.body:
                     process_assign(s)
                 pblocks = stmt.loop_body.copy()
-                push_call_vars(pblocks, saved_globals, saved_getattrs, nested=True)
+                push_call_vars(pblocks, saved_globals, saved_getattrs, typemap, nested=True)
                 new_body.append(stmt)
                 continue
             else:
                 process_assign(stmt)
             for v in stmt.list_vars():
                 new_body += _get_saved_call_nodes(v.name, saved_globals,
-                                                  saved_getattrs, block_defs)
+                                                  saved_getattrs, block_defs, rename_dict)
             new_body.append(stmt)
         block.body = new_body
+        # If there is anything to rename then apply the renaming here.
+        if len(rename_dict) > 0:
+            # Fix-up the typing for the renamed vars.
+            for k, v in rename_dict.items():
+                typemap[v] = typemap[k]
+            # This is only to call replace_var_names which takes a dict.
+            temp_blocks = {0: block}
+            replace_var_names(temp_blocks, rename_dict)
 
     return
 
 
-def _get_saved_call_nodes(fname, saved_globals, saved_getattrs, block_defs):
+def _get_saved_call_nodes(fname, saved_globals, saved_getattrs, block_defs, rename_dict):
+    """ Implement the copying of globals or getattrs for the purposes noted in
+        push_call_vars.  We make a new var and assign to it a copy of the
+        global or getattr.  We remember this new assignment node and add an
+        entry in the renaming dictionary so that for this block the original
+        var name is replaced by the new var name we created.
+    """
     nodes = []
     while (fname not in block_defs and (fname in saved_globals
                                         or fname in saved_getattrs)):
+        def rename_global_or_getattr(obj, var_base, nodes, block_defs, rename_dict):
+            assert(isinstance(obj, ir.Assign))
+            renamed_var = ir.Var(obj.target.scope,
+                                 mk_unique_var(var_base),
+                                 obj.target.loc)
+            renamed_assign = ir.Assign(copy.deepcopy(obj.value),
+                                       renamed_var,
+                                       obj.loc)
+            nodes.append(renamed_assign)
+            block_defs.add(obj.target.name)
+            rename_dict[obj.target.name] = renamed_assign.target.name
+
         if fname in saved_globals:
-            nodes.append(saved_globals[fname])
-            block_defs.add(saved_globals[fname].target.name)
+            rename_global_or_getattr(saved_globals[fname], "$push_global_to_block",
+                                     nodes, block_defs, rename_dict)
             fname = '_PA_DONE'
         elif fname in saved_getattrs:
-            up_name = saved_getattrs[fname].value.value.name
-            nodes.append(saved_getattrs[fname])
-            block_defs.add(saved_getattrs[fname].target.name)
-            fname = up_name
+            rename_global_or_getattr(saved_getattrs[fname], "$push_getattr_to_block",
+                                     nodes, block_defs, rename_dict)
+            fname = saved_getattrs[fname].value.value.name
     nodes.reverse()
     return nodes
 


Test patch:
diff --git a/numba/tests/test_parfors.py b/numba/tests/test_parfors.py
--- a/numba/tests/test_parfors.py
+++ b/numba/tests/test_parfors.py
@@ -2320,6 +2320,30 @@ def test_impl():
             return B
         self.prange_tester(test_impl)
 
+    @skip_unsupported
+    def test_copy_global_for_parfor(self):
+        """ issue4903: a global is copied next to a parfor so that
+            it can be inlined into the parfor and thus not have to be
+            passed to the parfor (i.e., an unsupported function type).
+            This global needs to be renamed in the block into which
+            it is copied.
+        """
+        def test_impl(zz, tc):
+            lh = np.zeros(len(tc))
+            lc = np.zeros(len(tc))
+            for i in range(1):
+                nt = tc[i]
+                for t in range(nt):
+                    lh += np.exp(zz[i, t])
+                for t in range(nt):
+                    lc += np.exp(zz[i, t])
+            return lh, lc
+
+        m = 2
+        zz = np.ones((m, m, m))
+        tc = np.ones(m, dtype=np.int_)
+        self.prange_tester(test_impl, zz, tc, patch_instance=[0])
+
     @skip_unsupported
     def test_multiple_call_getattr_object(self):
         def test_impl(n):