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

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

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

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

    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,))
        return self.context.cast(self.builder, res, signature.return_type, resty)

    elif expr.op == "exhaust_iter":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        # If we have a heterogenous tuple, we needn't do anything,
        # and we can't iterate over it anyway.
        if isinstance(ty, types.Tuple):
            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)
            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)

        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
            res = self.context.get_bound_function(self.builder, val, ty)
        else:
            impl = self.context.get_attribute(val, ty, expr.attr)

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

    elif expr.op == "static_getitem":
        baseval = self.loadvar(expr.value.name)
        indexval = self.context.get_constant(types.intp, expr.index)
        if cgutils.is_struct(baseval.type):
            # Statically extract the given element from the structure
            # (structures aren't dynamically indexable).
            return self.builder.extract_value(baseval, expr.index)
        else:
            # Fall back on the generic getitem() implementation
            # for this type.
            signature = typing.signature(
                resty, self.typeof(expr.value.name), types.intp
            )
            impl = self.context.get_function("getitem", signature)
            argvals = (baseval, indexval)
            res = impl(self.builder, argvals)
            return self.context.cast(self.builder, res, signature.return_type, resty)

    elif expr.op == "getitem":
        baseval = self.loadvar(expr.value.name)
        indexval = self.loadvar(expr.index.name)
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function("getitem", signature)
        argvals = (baseval, indexval)
        argtyps = (self.typeof(expr.value.name), self.typeof(expr.index.name))
        castvals = [
            self.context.cast(self.builder, av, at, ft)
            for av, at, ft in zip(argvals, argtyps, signature.args)
        ]
        res = impl(self.builder, castvals)
        return self.context.cast(self.builder, res, signature.return_type, resty)

    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.get_constant_undef(resty)
        for i in range(len(castvals)):
            tup = self.builder.insert_value(tup, castvals[i], i)
        return tup

    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)
        return castval

    raise NotImplementedError(expr)


# Traceback (numba.lowering.LoweringError)
ff = jit(f, nopython=True)
ff((4,5,6,7))
(4, 5, 6)
ff((4,5))
Traceback (most recent call last):
[...]
numba.lowering.LoweringError: Failed at nopython (nopython mode backend)
Internal error:
TypeError: Can't index at [2] in [2 x i32]
File "<stdin>", line 2

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

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

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

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

    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,))
        return self.context.cast(self.builder, res, signature.return_type, resty)

    elif expr.op == "exhaust_iter":
        val = self.loadvar(expr.value.name)
        ty = self.typeof(expr.value.name)
        # If we have a tuple, we needn't do anything
        # (and we can't iterate over the heterogenous ones).
        if isinstance(ty, types.BaseTuple):
            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)
            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)

        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
            res = self.context.get_bound_function(self.builder, val, ty)
        else:
            impl = self.context.get_attribute(val, ty, expr.attr)

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

    elif expr.op == "static_getitem":
        baseval = self.loadvar(expr.value.name)
        indexval = self.context.get_constant(types.intp, expr.index)
        if cgutils.is_struct(baseval.type):
            # Statically extract the given element from the structure
            # (structures aren't dynamically indexable).
            return self.builder.extract_value(baseval, expr.index)
        else:
            # Fall back on the generic getitem() implementation
            # for this type.
            signature = typing.signature(
                resty, self.typeof(expr.value.name), types.intp
            )
            impl = self.context.get_function("getitem", signature)
            argvals = (baseval, indexval)
            res = impl(self.builder, argvals)
            return self.context.cast(self.builder, res, signature.return_type, resty)

    elif expr.op == "getitem":
        baseval = self.loadvar(expr.value.name)
        indexval = self.loadvar(expr.index.name)
        signature = self.fndesc.calltypes[expr]
        impl = self.context.get_function("getitem", signature)
        argvals = (baseval, indexval)
        argtyps = (self.typeof(expr.value.name), self.typeof(expr.index.name))
        castvals = [
            self.context.cast(self.builder, av, at, ft)
            for av, at, ft in zip(argvals, argtyps, signature.args)
        ]
        res = impl(self.builder, castvals)
        return self.context.cast(self.builder, res, signature.return_type, resty)

    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.get_constant_undef(resty)
        for i in range(len(castvals)):
            tup = self.builder.insert_value(tup, castvals[i], i)
        return tup

    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)
        return castval

    raise NotImplementedError(expr)