Repository: numba/numba

Issue:
Constant structured type causes incorrect attempt at code generation
Example code (written as an attempt to demonstrate an example for #1071):

``` python
import numpy as np
from numba import njit

def make_numba_enum(**kwargs):
    dtype = np.dtype([ (k, np.int32) for k in kwargs ])
    enum =  np.recarray(1, dtype)[0]
    for k, v in kwargs.items():
        enum[k] = v
    return enum

some_constants_container = make_numba_enum(
    SOME_NAME = 0x1,
    SOME_OTHER_CONSTANT = 0x2,
    AND_ANOTHER_CONSTANT = 0x4,
)

@njit
def has_some_other(x):
    return (x & some_constants_container.SOME_OTHER_CONSTANT) != 0

print(has_some_other(some_constants_container.SOME_NAME))
print(has_some_other(some_constants_container.SOME_NAME |
                     some_constants_container.SOME_OTHER_CONSTANT))
```

Results in the following traceback:

```
$ python e2.py 
Traceback (most recent call last):
  File "/home/gmarkall/work/numba/numba/lowering.py", line 148, in lower_block
    self.lower_inst(inst)
  File "/home/gmarkall/work/numba/numba/lowering.py", line 188, in lower_inst
    val = self.lower_assign(ty, inst)
  File "/home/gmarkall/work/numba/numba/lowering.py", line 320, in lower_assign
    return self.lower_expr(ty, value)
  File "/home/gmarkall/work/numba/numba/lowering.py", line 552, in lower_expr
    res = impl(self.context, self.builder, ty, val, expr.attr)
  File "/home/gmarkall/work/numba/numba/targets/imputils.py", line 32, in res
    ret = real_impl(context, builder, typ, value)
  File "/home/gmarkall/work/numba/numba/targets/base.py", line 488, in imp
    self.get_data_type(elemty))
  File "/home/gmarkall/work/numba/numba/cgutils.py", line 716, in get_record_member
    pval = inbound_gep(builder, record, 0, offset)
  File "/home/gmarkall/work/numba/numba/cgutils.py", line 733, in inbound_gep
    return builder.gep(ptr, idx, inbounds=True)
  File "/home/gmarkall/.conda/envs/p34/lib/python3.4/site-packages/llvmlite/ir/builder.py", line 366, in gep
    inbounds=inbounds, name=name)
  File "/home/gmarkall/.conda/envs/p34/lib/python3.4/site-packages/llvmlite/ir/instructions.py", line 319, in __init__
    lasttyp, typ = typ, typ.gep(i)
AttributeError: 'IntType' object has no attribute 'gep'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "e2.py", line 21, in <module>
    print(has_some_other(some_constants_container.SOME_NAME))
  File "/home/gmarkall/work/numba/numba/dispatcher.py", line 162, in _compile_for_args
    return self.compile(sig)
  File "/home/gmarkall/work/numba/numba/dispatcher.py", line 305, in compile
    flags=flags, locals=self.locals)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 563, in compile_extra
    return pipeline.compile_extra(func)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 301, in compile_extra
    return self.compile_bytecode(bc, func_attr=self.func_attr)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 310, in compile_bytecode
    return self._compile_bytecode()
  File "/home/gmarkall/work/numba/numba/compiler.py", line 550, in _compile_bytecode
    return pm.run(self.status)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 199, in run
    raise patched_exception
  File "/home/gmarkall/work/numba/numba/compiler.py", line 191, in run
    res = stage()
  File "/home/gmarkall/work/numba/numba/compiler.py", line 508, in stage_nopython_backend
    return self._backend(lowerfn, objectmode=False)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 463, in _backend
    lowered = lowerfn()
  File "/home/gmarkall/work/numba/numba/compiler.py", line 454, in backend_nopython_mode
    self.flags)
  File "/home/gmarkall/work/numba/numba/compiler.py", line 705, in native_lowering_stage
    lower.lower()
  File "/home/gmarkall/work/numba/numba/lowering.py", line 77, in lower
    self.lower_normal_function(self.fndesc)
  File "/home/gmarkall/work/numba/numba/lowering.py", line 109, in lower_normal_function
    entry_block_tail = self.lower_function_body()
  File "/home/gmarkall/work/numba/numba/lowering.py", line 135, in lower_function_body
    self.lower_block(block)
  File "/home/gmarkall/work/numba/numba/lowering.py", line 153, in lower_block
    raise LoweringError(msg, inst.loc)
numba.lowering.LoweringError: Failed at nopython (nopython mode backend)
Internal error:
AttributeError: 'IntType' object has no attribute 'gep'
File "e2.py", line 19
```



Discussion:

Resolving patch:
diff --git a/numba/targets/base.py b/numba/targets/base.py
--- a/numba/targets/base.py
+++ b/numba/targets/base.py
@@ -315,7 +315,13 @@ def get_constant_generic(self, builder, ty, val):
         Return a LLVM constant representing value *val* of Numba type *ty*.
         """
         if self.is_struct_type(ty):
-            return self.get_constant_struct(builder, ty, val)
+            struct = self.get_constant_struct(builder, ty, val)
+            if isinstance(ty, types.Record):
+                ptrty = self.data_model_manager[ty].get_data_type()
+                ptr = cgutils.alloca_once(builder, ptrty)
+                builder.store(struct, ptr)
+                return ptr
+            return struct
 
         elif isinstance(ty, types.ExternalFunctionPointer):
             ptrty = self.get_function_pointer_type(ty)
@@ -479,7 +485,7 @@ def imp(context, builder, typ, val):
                         itemsize=context.get_constant(types.intp, elemty.size),
                         parent=ary.parent
                     )
-                    
+
                     return ary._getvalue()
             else:
                 @impl_attribute(typ, attr, elemty)


Test patch:
diff --git a/numba/tests/test_globals.py b/numba/tests/test_globals.py
--- a/numba/tests/test_globals.py
+++ b/numba/tests/test_globals.py
@@ -58,6 +58,16 @@ def global_two_rec_arrs(a, b, c, d):
         d[i] = rec_Y[i].d
 
 
+# Test a global record
+record_only_X = np.recarray(1, dtype=x_dt)[0]
+record_only_X.a = 1
+record_only_X.b = 1.5
+
+@jit(nopython=True)
+def global_record_func(x):
+    return x.a == record_only_X.a
+
+
 @jit(nopython=True)
 def global_module_func(x, y):
     return usecases.andornopython(x, y)
@@ -147,5 +157,15 @@ def test_global_module(self):
         res = global_module_func(5, 6)
         self.assertEqual(True, res)
 
+    def test_global_record(self):
+        # (see github issue #1081)
+        x = np.recarray(1, dtype=x_dt)[0]
+        x.a = 1
+        res = global_record_func(x)
+        self.assertEqual(True, res)
+        x.a = 2
+        res = global_record_func(x)
+        self.assertEqual(False, res)
+
 if __name__ == '__main__':
     unittest.main()