# Buggy code
def lower_expr(self, resty, expr):
    if expr.op == "binop":
        return self.lower_binop(resty, expr, expr.fn)
    elif expr.op == "inplace_binop":
        lty = self.typeof(expr.lhs.name)
        if lty.mutable:
            return self.lower_binop(resty, expr, expr.fn)
        else:
            # inplace operators on non-mutable types reuse the same
            # definition as the corresponding copying operators.)
            return self.lower_binop(resty, expr, expr.immutable_fn)
    elif expr.op == "unary":
        val = self.loadvar(expr.value.name)
        typ = self.typeof(expr.value.name)
        func_ty = self.context.typing_context.resolve_value_type(expr.fn)
        # Get function
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function(func_ty, signature)
        # Convert argument to match
        val = self.context.cast(self.builder, val, typ, signature.args[0])
        res = impl(self.builder, [val])
        res = self.context.cast(self.builder, res, signature.return_type, resty)
        return res

    elif expr.op == "call":
        res = self.lower_call(resty, expr)
        return res

    elif expr.op == "pair_first":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        res = self.context.pair_first(self.builder, val, ty)
        self.incref(resty, res)
        return res

    elif expr.op == "pair_second":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        res = self.context.pair_second(self.builder, val, ty)
        self.incref(resty, res)
        return res

    elif expr.op in ("getiter", "iternext"):
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function(expr.op, signature)
        [fty] = signature.args
        castval = self.context.cast(self.builder, val, ty, fty)
        res = impl(self.builder, (castval,))
        res = self.context.cast(self.builder, res, signature.return_type, resty)
        return res

    elif expr.op == "exhaust_iter":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        # Unpack optional
        if isinstance(ty, types.Optional):
            val = self.context.cast(self.builder, val, ty, ty.type)
            ty = ty.type

        # If we have a tuple, we needn't do anything
        # (and we can't iterate over the heterogeneous ones).
        if isinstance(ty, types.BaseTuple):
            assert ty == resty
            self.incref(ty, val)
            return val

        itemty = ty.iterator_type.yield_type
        tup = self.context.get_constant_undef(resty)
        pairty = types.Pair(itemty, types.boolean)
        getiter_sig = typing.signature(ty.iterator_type, ty)
        getiter_impl = self.context.get_function("getiter", getiter_sig)
        iternext_sig = typing.signature(pairty, ty.iterator_type)
        iternext_impl = self.context.get_function("iternext", iternext_sig)
        iterobj = getiter_impl(self.builder, (val,))
        # We call iternext() as many times as desired (`expr.count`).
        for i in range(expr.count):
            pair = iternext_impl(self.builder, (iterobj,))
            is_valid = self.context.pair_second(self.builder, pair, pairty)
            with cgutils.if_unlikely(self.builder, self.builder.not_(is_valid)):
                self.return_exception(ValueError, loc=self.loc)
            item = self.context.pair_first(self.builder, pair, pairty)
            tup = self.builder.insert_value(tup, item, i)

        # Call iternext() once more to check that the iterator
        # is exhausted.
        pair = iternext_impl(self.builder, (iterobj,))
        is_valid = self.context.pair_second(self.builder, pair, pairty)
        with cgutils.if_unlikely(self.builder, is_valid):
            self.return_exception(ValueError, loc=self.loc)

        self.decref(ty.iterator_type, iterobj)
        return tup

    elif expr.op == "getattr":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)

        if isinstance(resty, types.BoundFunction):
            # if we are getting out a method, assume we have typed this
            # properly and just build a bound function object
            casted = self.context.cast(self.builder, val, ty, resty.this)
            res = self.context.get_bound_function(self.builder, casted, resty.this)
            self.incref(resty, res)
            return res
        else:
            impl = self.context.get_getattr(ty, expr.attr)
            attrty = self.context.typing_context.resolve_getattr(ty, expr.attr)

            if impl is None:
                # ignore the attribute
                return self.context.get_dummy_value()
            else:
                res = impl(self.context, self.builder, ty, val, expr.attr)

            # Cast the attribute type to the expected output type
            res = self.context.cast(self.builder, res, attrty, resty)
            return res

    elif expr.op == "static_getitem":
        signature = typing.signature(
            resty,
            self.typeof(expr.value.name),
            _lit_or_omitted(expr.index),
        )
        try:
            # Both get_function() and the returned implementation can
            # raise NotImplementedError if the types aren't supported
            impl = self.context.get_function("static_getitem", signature)
            return impl(self.builder, (self.loadvar(expr.value.name), expr.index))
        except NotImplementedError:
            if expr.index_var is None:
                raise
            # Fall back on the generic getitem() implementation
            # for this type.
            signature = self.fndesc.calltypes[expr]
            return self.lower_getitem(
                resty, expr, expr.value, expr.index_var, signature
            )
    elif expr.op == "typed_getitem":
        signature = typing.signature(
            resty,
            self.typeof(expr.value.name),
            self.typeof(expr.index.name),
        )
        impl = self.context.get_function("typed_getitem", signature)
        return impl(
            self.builder, (self.loadvar(expr.value.name), self.loadvar(expr.index.name))
        )
    elif expr.op == "getitem":
        signature = self.fndesc.calltypes[expr]
        return self.lower_getitem(resty, expr, expr.value, expr.index, signature)

    elif expr.op == "build_tuple":
        itemvals = [self.loadvar(i.name) for i in expr.items]
        itemtys = [self.typeof(i.name) for i in expr.items]
        castvals = [
            self.context.cast(self.builder, val, fromty, toty)
            for val, toty, fromty in zip(itemvals, resty, itemtys)
        ]
        tup = self.context.make_tuple(self.builder, resty, castvals)
        self.incref(resty, tup)
        return tup

    elif expr.op == "build_list":
        itemvals = [self.loadvar(i.name) for i in expr.items]
        itemtys = [self.typeof(i.name) for i in expr.items]
        castvals = [
            self.context.cast(self.builder, val, fromty, resty.dtype)
            for val, fromty in zip(itemvals, itemtys)
        ]
        return self.context.build_list(self.builder, resty, castvals)

    elif expr.op == "build_set":
        # Insert in reverse order, as Python does
        items = expr.items[::-1]
        itemvals = [self.loadvar(i.name) for i in items]
        itemtys = [self.typeof(i.name) for i in items]
        castvals = [
            self.context.cast(self.builder, val, fromty, resty.dtype)
            for val, fromty in zip(itemvals, itemtys)
        ]
        return self.context.build_set(self.builder, resty, castvals)

    elif expr.op == "build_map":
        items = expr.items
        keys, values = [], []
        key_types, value_types = [], []
        for k, v in items:
            key = self.loadvar(k.name)
            keytype = self.typeof(k.name)
            val = self.loadvar(v.name)
            valtype = self.typeof(v.name)
            keys.append(key)
            values.append(val)
            key_types.append(keytype)
            value_types.append(valtype)
        return self.context.build_map(
            self.builder,
            resty,
            list(zip(key_types, value_types)),
            list(zip(keys, values)),
        )

    elif expr.op == "cast":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        castval = self.context.cast(self.builder, val, ty, resty)
        self.incref(resty, castval)
        return castval

    elif expr.op == "phi":
        raise LoweringError("PHI not stripped")

    elif expr.op in self.context.special_ops:
        res = self.context.special_ops[expr.op](self, expr)
        return res

    raise NotImplementedError(expr)


# Traceback (KeyError)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/anaconda3/envs/latest_versions38/lib/python3.8/site-packages/numba/core/errors.py in new_error_context(fmt_, *args, **kwargs)
768     try:
--> 769         yield
770     except NumbaError as e:

~/anaconda3/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/site-packages/numba/core/lowering.py in lower_assign(self, ty, inst)
540         elif isinstance(value, ir.Var):
--> 541             val = self.loadvar(value.name)
542             oty = self.typeof(value.name)

~/anaconda3/envs/latest_versions38/lib/python3.8/site-packages/numba/core/lowering.py in loadvar(self, name)
1280         """
-> 1281         ptr = self.getvar(name)
1282         return self.builder.load(ptr)

~/anaconda3/envs/latest_versions38/lib/python3.8/site-packages/numba/core/lowering.py in getvar(self, name)
1274         """
-> 1275         return self.varmap[name]
1276

KeyError: 'cum_v.2'

During handling of the above exception, another exception occurred:

LoweringError                             Traceback (most recent call last)
<ipython-input-19-fa852184280b> in <module>
31     return probs
32
---> 33 foo(pol_data, decr_vecs)

~/anaconda3/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_core(self, args, return_type)
102
103         impl = self._get_implementation(args, {})
--> 104         cres = compiler.compile_extra(self.targetdescr.typing_context,
105                                       self.targetdescr.target_context,
106                                       impl,

~/anaconda3/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/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/envs/latest_versions38/lib/python3.8/contextlib.py in __exit__(self, type, value, traceback)
129                 value = type()
130             try:
--> 131                 self.gen.throw(type, value, traceback)
132             except StopIteration as exc:
133                 # Suppress StopIteration *unless* it's the same exception that

~/anaconda3/envs/latest_versions38/lib/python3.8/site-packages/numba/core/errors.py in new_error_context(fmt_, *args, **kwargs)
774         newerr = errcls(e).add_context(_format_msg(fmt_, args, kwargs))
775         tb = sys.exc_info()[2] if numba.core.config.FULL_TRACEBACKS else None
--> 776         reraise(type(newerr), newerr, tb)
777
778

~/anaconda3/envs/latest_versions38/lib/python3.8/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)
'cum_v.2'

File "<ipython-input-19-fa852184280b>", line 30:
def foo(pol_data, decr_vecs):
<source elided>
else:
cum_v = np.kron(cum_v, v)
^

[1] During: lowering "cum_v.4 = cum_v.2" at <ipython-input-19-fa852184280b> (30)

# Fixed code
def lower_expr(self, resty, expr):
    if expr.op == "binop":
        return self.lower_binop(resty, expr, expr.fn)
    elif expr.op == "inplace_binop":
        lty = self.typeof(expr.lhs.name)
        if lty.mutable:
            return self.lower_binop(resty, expr, expr.fn)
        else:
            # inplace operators on non-mutable types reuse the same
            # definition as the corresponding copying operators.)
            return self.lower_binop(resty, expr, expr.immutable_fn)
    elif expr.op == "unary":
        val = self.loadvar(expr.value.name)
        typ = self.typeof(expr.value.name)
        func_ty = self.context.typing_context.resolve_value_type(expr.fn)
        # Get function
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function(func_ty, signature)
        # Convert argument to match
        val = self.context.cast(self.builder, val, typ, signature.args[0])
        res = impl(self.builder, [val])
        res = self.context.cast(self.builder, res, signature.return_type, resty)
        return res

    elif expr.op == "call":
        res = self.lower_call(resty, expr)
        return res

    elif expr.op == "pair_first":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        res = self.context.pair_first(self.builder, val, ty)
        self.incref(resty, res)
        return res

    elif expr.op == "pair_second":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        res = self.context.pair_second(self.builder, val, ty)
        self.incref(resty, res)
        return res

    elif expr.op in ("getiter", "iternext"):
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function(expr.op, signature)
        [fty] = signature.args
        castval = self.context.cast(self.builder, val, ty, fty)
        res = impl(self.builder, (castval,))
        res = self.context.cast(self.builder, res, signature.return_type, resty)
        return res

    elif expr.op == "exhaust_iter":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        # Unpack optional
        if isinstance(ty, types.Optional):
            val = self.context.cast(self.builder, val, ty, ty.type)
            ty = ty.type

        # If we have a tuple, we needn't do anything
        # (and we can't iterate over the heterogeneous ones).
        if isinstance(ty, types.BaseTuple):
            assert ty == resty
            self.incref(ty, val)
            return val

        itemty = ty.iterator_type.yield_type
        tup = self.context.get_constant_undef(resty)
        pairty = types.Pair(itemty, types.boolean)
        getiter_sig = typing.signature(ty.iterator_type, ty)
        getiter_impl = self.context.get_function("getiter", getiter_sig)
        iternext_sig = typing.signature(pairty, ty.iterator_type)
        iternext_impl = self.context.get_function("iternext", iternext_sig)
        iterobj = getiter_impl(self.builder, (val,))
        # We call iternext() as many times as desired (`expr.count`).
        for i in range(expr.count):
            pair = iternext_impl(self.builder, (iterobj,))
            is_valid = self.context.pair_second(self.builder, pair, pairty)
            with cgutils.if_unlikely(self.builder, self.builder.not_(is_valid)):
                self.return_exception(ValueError, loc=self.loc)
            item = self.context.pair_first(self.builder, pair, pairty)
            tup = self.builder.insert_value(tup, item, i)

        # Call iternext() once more to check that the iterator
        # is exhausted.
        pair = iternext_impl(self.builder, (iterobj,))
        is_valid = self.context.pair_second(self.builder, pair, pairty)
        with cgutils.if_unlikely(self.builder, is_valid):
            self.return_exception(ValueError, loc=self.loc)

        self.decref(ty.iterator_type, iterobj)
        return tup

    elif expr.op == "getattr":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)

        if isinstance(resty, types.BoundFunction):
            # if we are getting out a method, assume we have typed this
            # properly and just build a bound function object
            casted = self.context.cast(self.builder, val, ty, resty.this)
            res = self.context.get_bound_function(self.builder, casted, resty.this)
            self.incref(resty, res)
            return res
        else:
            impl = self.context.get_getattr(ty, expr.attr)
            attrty = self.context.typing_context.resolve_getattr(ty, expr.attr)

            if impl is None:
                # ignore the attribute
                return self.context.get_dummy_value()
            else:
                res = impl(self.context, self.builder, ty, val, expr.attr)

            # Cast the attribute type to the expected output type
            res = self.context.cast(self.builder, res, attrty, resty)
            return res

    elif expr.op == "static_getitem":
        signature = typing.signature(
            resty,
            self.typeof(expr.value.name),
            _lit_or_omitted(expr.index),
        )
        try:
            # Both get_function() and the returned implementation can
            # raise NotImplementedError if the types aren't supported
            impl = self.context.get_function("static_getitem", signature)
            return impl(self.builder, (self.loadvar(expr.value.name), expr.index))
        except NotImplementedError:
            if expr.index_var is None:
                raise
            # Fall back on the generic getitem() implementation
            # for this type.
            signature = self.fndesc.calltypes[expr]
            return self.lower_getitem(
                resty, expr, expr.value, expr.index_var, signature
            )
    elif expr.op == "typed_getitem":
        signature = typing.signature(
            resty,
            self.typeof(expr.value.name),
            self.typeof(expr.index.name),
        )
        impl = self.context.get_function("typed_getitem", signature)
        return impl(
            self.builder, (self.loadvar(expr.value.name), self.loadvar(expr.index.name))
        )
    elif expr.op == "getitem":
        signature = self.fndesc.calltypes[expr]
        return self.lower_getitem(resty, expr, expr.value, expr.index, signature)

    elif expr.op == "build_tuple":
        itemvals = [self.loadvar(i.name) for i in expr.items]
        itemtys = [self.typeof(i.name) for i in expr.items]
        castvals = [
            self.context.cast(self.builder, val, fromty, toty)
            for val, toty, fromty in zip(itemvals, resty, itemtys)
        ]
        tup = self.context.make_tuple(self.builder, resty, castvals)
        self.incref(resty, tup)
        return tup

    elif expr.op == "build_list":
        itemvals = [self.loadvar(i.name) for i in expr.items]
        itemtys = [self.typeof(i.name) for i in expr.items]
        castvals = [
            self.context.cast(self.builder, val, fromty, resty.dtype)
            for val, fromty in zip(itemvals, itemtys)
        ]
        return self.context.build_list(self.builder, resty, castvals)

    elif expr.op == "build_set":
        # Insert in reverse order, as Python does
        items = expr.items[::-1]
        itemvals = [self.loadvar(i.name) for i in items]
        itemtys = [self.typeof(i.name) for i in items]
        castvals = [
            self.context.cast(self.builder, val, fromty, resty.dtype)
            for val, fromty in zip(itemvals, itemtys)
        ]
        return self.context.build_set(self.builder, resty, castvals)

    elif expr.op == "build_map":
        items = expr.items
        keys, values = [], []
        key_types, value_types = [], []
        for k, v in items:
            key = self.loadvar(k.name)
            keytype = self.typeof(k.name)
            val = self.loadvar(v.name)
            valtype = self.typeof(v.name)
            keys.append(key)
            values.append(val)
            key_types.append(keytype)
            value_types.append(valtype)
        return self.context.build_map(
            self.builder,
            resty,
            list(zip(key_types, value_types)),
            list(zip(keys, values)),
        )

    elif expr.op == "cast":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        castval = self.context.cast(self.builder, val, ty, resty)
        self.incref(resty, castval)
        return castval

    elif expr.op == "phi":
        raise LoweringError("PHI not stripped")

    elif expr.op == "null":
        return self.context.get_constant_null(resty)

    elif expr.op in self.context.special_ops:
        res = self.context.special_ops[expr.op](self, expr)
        return res

    raise NotImplementedError(expr)