# Buggy code
def slice_size(self, index, dsize, equiv_set, scope, stmts):
    """Reason about the size of a slice represented by the "index"
    variable, and return a variable that has this size data, or
    raise GuardException if it cannot reason about it.

    The computation takes care of negative values used in the slice
    with respect to the given dimensional size ("dsize").

    Extra statments required to produce the result are appended
    to parent function's stmts list.
    """
    loc = index.loc
    # Get the definition of the index variable.
    index_def = get_definition(self.func_ir, index)
    fname, mod_name = find_callname(self.func_ir, index_def, typemap=self.typemap)
    require(fname == "slice" and mod_name in ("__builtin__", "builtins"))
    require(len(index_def.args) == 2)
    lhs = index_def.args[0]
    rhs = index_def.args[1]
    size_typ = self.typemap[dsize.name]
    lhs_typ = self.typemap[lhs.name]
    rhs_typ = self.typemap[rhs.name]

    if config.DEBUG_ARRAY_OPT >= 2:
        print(
            "slice_size",
            "index",
            index,
            "dsize",
            dsize,
            "index_def",
            index_def,
            "lhs",
            lhs,
            "rhs",
            rhs,
            "size_typ",
            size_typ,
            "lhs_typ",
            lhs_typ,
            "rhs_typ",
            rhs_typ,
        )

    # Fill in the left side of the slice's ":" with 0 if it wasn't specified.
    if isinstance(lhs_typ, types.NoneType):
        zero_var = ir.Var(scope, mk_unique_var("zero"), loc)
        zero = ir.Const(0, loc)
        stmts.append(ir.Assign(value=zero, target=zero_var, loc=loc))
        self._define(equiv_set, zero_var, types.IntegerLiteral(0), zero)
        lhs = zero_var
        lhs_typ = types.IntegerLiteral(0)

    # Fill in the right side of the slice's ":" with the array
    # length if it wasn't specified.
    if isinstance(rhs_typ, types.NoneType):
        rhs = dsize
        rhs_typ = size_typ

    lhs_rel = equiv_set.get_rel(lhs)
    rhs_rel = equiv_set.get_rel(rhs)
    if config.DEBUG_ARRAY_OPT >= 2:
        print("lhs_rel", lhs_rel, "rhs_rel", rhs_rel)

    # Make a deepcopy of the original slice to use as the
    # replacement slice, which we will modify as necessary
    # below to convert all negative constants in the slice
    # to be relative to the dimension size.
    replacement_slice = copy.deepcopy(index_def)
    need_replacement = False

    # If the first part of the slice is a constant N then check if N
    # is negative.  If so, then rewrite the first part of the slice
    # to be "dsize - N".  This is necessary because later steps will
    # try to compute slice size with a subtraction which wouldn't work
    # if any part of the slice was negative.
    if isinstance(lhs_rel, int):
        if lhs_rel < 0:
            # Indicate we will need to replace the slice var.
            need_replacement = True
            explicit_neg_var, explicit_neg_typ = self.gen_explicit_neg(
                lhs, lhs_rel, lhs_typ, size_typ, loc, scope, dsize, stmts, equiv_set
            )
            replacement_slice.args = (explicit_neg_var, rhs)
            # Update lhs information with the negative removed.
            lhs = replacement_slice.args[0]
            lhs_typ = explicit_neg_typ
            lhs_rel = equiv_set.get_rel(lhs)

    # If the second part of the slice is a constant N then check if N
    # is negative.  If so, then rewrite the second part of the slice
    # to be "dsize - N".  This is necessary because later steps will
    # try to compute slice size with a subtraction which wouldn't work
    # if any part of the slice was negative.
    if isinstance(rhs_rel, int):
        if rhs_rel < 0:
            # Indicate we will need to replace the slice var.
            need_replacement = True
            explicit_neg_var, explicit_neg_typ = self.gen_explicit_neg(
                rhs, rhs_rel, rhs_typ, size_typ, loc, scope, dsize, stmts, equiv_set
            )
            replacement_slice.args = (lhs, explicit_neg_var)
            # Update rhs information with the negative removed.
            rhs = replacement_slice.args[1]
            rhs_typ = explicit_neg_typ
            rhs_rel = equiv_set.get_rel(rhs)

    # If neither of the parts of the slice were negative constants
    # then we don't need to do slice replacement in the IR.
    if not need_replacement:
        replacement_slice_var = None
    else:
        # Create a new var for the replacement slice.
        replacement_slice_var = ir.Var(scope, mk_unique_var("replacement_slice"), loc)
        # Create a deepcopy of slice calltype so that when we change it below
        # the original isn't changed.  Make the types of the parts of the slice
        # intp.
        new_arg_typs = (types.intp, types.intp)
        rs_calltype = self.typemap[index_def.func.name].get_call_type(
            self.context, new_arg_typs, {}
        )
        self.calltypes[replacement_slice] = rs_calltype
        stmts.append(
            ir.Assign(value=replacement_slice, target=replacement_slice_var, loc=loc)
        )
        # The type of the replacement slice is the same type as the original.
        self.typemap[replacement_slice_var.name] = self.typemap[index.name]

    if config.DEBUG_ARRAY_OPT >= 2:
        print("after rewriting negatives", "lhs_rel", lhs_rel, "rhs_rel", rhs_rel)

    if (
        lhs_rel == 0
        and isinstance(rhs_rel, tuple)
        and equiv_set.is_equiv(dsize, rhs_rel[0])
        and rhs_rel[1] == 0
    ):
        return dsize, None

    slice_typ = types.intp

    size_var = ir.Var(scope, mk_unique_var("slice_size"), loc)
    size_val = ir.Expr.binop(operator.sub, rhs, lhs, loc=loc)
    self.calltypes[size_val] = signature(slice_typ, rhs_typ, lhs_typ)
    self._define(equiv_set, size_var, slice_typ, size_val)

    # short cut size_val to a constant if its relation is known to be
    # a constant
    size_rel = equiv_set.get_rel(size_var)
    if config.DEBUG_ARRAY_OPT >= 2:
        print("size_var", size_var, "size_val", size_val, "size_rel", size_rel)
    if isinstance(size_rel, int):
        size_val = ir.Const(size_rel, loc=loc)
        size_var = ir.Var(scope, mk_unique_var("slice_size"), loc)
        slice_typ = types.IntegerLiteral(size_rel)
        self._define(equiv_set, size_var, slice_typ, size_val)
        if config.DEBUG_ARRAY_OPT >= 2:
            print("inferred constant size", "size_var", size_var, "size_val", size_val)

    # rel_map keeps a map of relative sizes that we have seen so
    # that if we compute the same relative sizes different times
    # in different ways we can associate those two instances
    # of the same relative size to the same equivalence class.
    if isinstance(size_rel, tuple):
        if config.DEBUG_ARRAY_OPT >= 2:
            print("size_rel is tuple", size_rel in equiv_set.rel_map)
        if size_rel in equiv_set.rel_map:
            # We have seen this relative size before so establish
            # equivalence to the previous variable.
            if config.DEBUG_ARRAY_OPT >= 2:
                print("establishing equivalence to", equiv_set.rel_map[size_rel])
            equiv_set.insert_equiv(size_var, equiv_set.rel_map[size_rel])
        else:
            # The first time we've seen this relative size so
            # remember the variable defining that size.
            equiv_set.rel_map[size_rel] = size_var

    wrap_var = ir.Var(scope, mk_unique_var("wrap"), loc)
    wrap_def = ir.Global("wrap_index", wrap_index, loc=loc)
    fnty = get_global_func_typ(wrap_index)
    sig = self.context.resolve_function_type(
        fnty,
        (
            slice_typ,
            size_typ,
        ),
        {},
    )
    self._define(equiv_set, wrap_var, fnty, wrap_def)

    var = ir.Var(scope, mk_unique_var("var"), loc)
    value = ir.Expr.call(wrap_var, [size_var, dsize], {}, loc)
    self._define(equiv_set, var, slice_typ, value)
    self.calltypes[value] = sig

    stmts.append(ir.Assign(value=size_val, target=size_var, loc=loc))
    stmts.append(ir.Assign(value=wrap_def, target=wrap_var, loc=loc))
    stmts.append(ir.Assign(value=value, target=var, loc=loc))
    return var, replacement_slice_var


# Traceback (IndexError)
Traceback (most recent call last):
File "/home/ibutygin/miniconda3/envs/NUMBA_CUSTOM/lib/python3.7/site-packages/llvmlite/ir/instructions.py", line 619, in __init__
typ = typ.elements[i]
IndexError: tuple index out of range

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/ibutygin/src/numba/numba/numba/errors.py", line 717, in new_error_context
yield
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 260, in lower_block
self.lower_inst(inst)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 303, in lower_inst
val = self.lower_assign(ty, inst)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 467, in lower_assign
return self.lower_expr(ty, value)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 932, in lower_expr
res = self.lower_call(resty, expr)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 724, in lower_call
res = self._lower_call_normal(fnty, expr, signature)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 903, in _lower_call_normal
res = impl(self.builder, argvals, self.loc)
File "/home/ibutygin/src/numba/numba/numba/targets/base.py", line 1132, in __call__
res = self._imp(self._context, builder, self._sig, args, loc=loc)
File "/home/ibutygin/src/numba/numba/numba/targets/base.py", line 1157, in wrapper
return fn(*args, **kwargs)
File "/home/ibutygin/src/numba/numba/numba/typing/templates.py", line 778, in method_impl
return call(builder, args)
File "/home/ibutygin/src/numba/numba/numba/targets/base.py", line 1132, in __call__
res = self._imp(self._context, builder, self._sig, args, loc=loc)
File "/home/ibutygin/src/numba/numba/numba/targets/base.py", line 1157, in wrapper
return fn(*args, **kwargs)
File "/home/ibutygin/src/numba/numba/numba/targets/imputils.py", line 193, in imp
builder, func, fndesc.restype, fndesc.argtypes, args)
File "/home/ibutygin/src/numba/numba/numba/targets/callconv.py", line 477, in call_function
args = list(arginfo.as_arguments(builder, args))
File "/home/ibutygin/src/numba/numba/numba/datamodel/packer.py", line 100, in as_arguments
for dm, val in zip(self._dm_args, values)
File "/home/ibutygin/src/numba/numba/numba/datamodel/packer.py", line 100, in <listcomp>
for dm, val in zip(self._dm_args, values)
File "/home/ibutygin/src/numba/numba/numba/datamodel/models.py", line 447, in as_argument
v = builder.extract_value(value, [i])
File "/home/ibutygin/miniconda3/envs/NUMBA_CUSTOM/lib/python3.7/site-packages/llvmlite/ir/builder.py", line 927, in extract_value
instr = instructions.ExtractValue(self.block, agg, idx, name=name)
File "/home/ibutygin/miniconda3/envs/NUMBA_CUSTOM/lib/python3.7/site-packages/llvmlite/ir/instructions.py", line 622, in __init__
% (list(indices), agg.type))
TypeError: Can't index at [0] in {}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "test.py", line 73, in <module>
print(bar(Dummy()))
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 420, in _compile_for_args
raise e
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 353, in _compile_for_args
return self.compile(tuple(argtypes))
File "/home/ibutygin/src/numba/numba/numba/compiler_lock.py", line 32, in _acquire_compile_lock
return func(*args, **kwargs)
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 768, in compile
cres = self._compiler.compile(args, return_type)
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 77, in compile
status, retval = self._compile_cached(args, return_type)
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 91, in _compile_cached
retval = self._compile_core(args, return_type)
File "/home/ibutygin/src/numba/numba/numba/dispatcher.py", line 109, in _compile_core
pipeline_class=self.pipeline_class)
File "/home/ibutygin/src/numba/numba/numba/compiler.py", line 543, in compile_extra
return pipeline.compile_extra(func)
File "/home/ibutygin/src/numba/numba/numba/compiler.py", line 328, in compile_extra
return self._compile_bytecode()
File "/home/ibutygin/src/numba/numba/numba/compiler.py", line 389, in _compile_bytecode
return self._compile_core()
File "/home/ibutygin/src/numba/numba/numba/compiler.py", line 368, in _compile_core
raise e
File "/home/ibutygin/src/numba/numba/numba/compiler.py", line 359, in _compile_core
pm.run(self.state)
File "/home/ibutygin/src/numba/numba/numba/compiler_machinery.py", line 331, in run
raise patched_exception
File "/home/ibutygin/src/numba/numba/numba/compiler_machinery.py", line 322, in run
self._runPass(idx, pass_inst, state)
File "/home/ibutygin/src/numba/numba/numba/compiler_lock.py", line 32, in _acquire_compile_lock
return func(*args, **kwargs)
File "/home/ibutygin/src/numba/numba/numba/compiler_machinery.py", line 281, in _runPass
mutated |= check(pss.run_pass, internal_state)
File "/home/ibutygin/src/numba/numba/numba/compiler_machinery.py", line 268, in check
mangled = func(compiler_state)
File "/home/ibutygin/src/numba/numba/numba/typed_passes.py", line 391, in run_pass
NativeLowering().run_pass(state)
File "/home/ibutygin/src/numba/numba/numba/typed_passes.py", line 333, in run_pass
lower.lower()
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 179, in lower
self.lower_normal_function(self.fndesc)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 220, in lower_normal_function
entry_block_tail = self.lower_function_body()
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 245, in lower_function_body
self.lower_block(block)
File "/home/ibutygin/src/numba/numba/numba/lowering.py", line 260, in lower_block
self.lower_inst(inst)
File "/home/ibutygin/miniconda3/envs/NUMBA_CUSTOM/lib/python3.7/contextlib.py", line 130, in __exit__
self.gen.throw(type, value, traceback)
File "/home/ibutygin/src/numba/numba/numba/errors.py", line 725, in new_error_context
six.reraise(type(newerr), newerr, tb)
File "/home/ibutygin/src/numba/numba/numba/six.py", line 669, in reraise
raise value
numba.errors.LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)
Can't index at [0] in {}

File "test.py", line 71:
def bar(obj):
return obj.inline_method(123,456)
^

[1] During: lowering "$10call_method.4 = call $4load_method.1($const6.2, $const8.3, func=$4load_method.1, args=[Var($const6.2, test.py:71), Var($const8.3, test.py:71)], kws=(), vararg=None)" at test.py (71)

# Fixed code
def slice_size(self, index, dsize, equiv_set, scope, stmts):
    """Reason about the size of a slice represented by the "index"
    variable, and return a variable that has this size data, or
    raise GuardException if it cannot reason about it.

    The computation takes care of negative values used in the slice
    with respect to the given dimensional size ("dsize").

    Extra statments required to produce the result are appended
    to parent function's stmts list.
    """
    loc = index.loc
    # Get the definition of the index variable.
    index_def = get_definition(self.func_ir, index)
    fname, mod_name = find_callname(self.func_ir, index_def, typemap=self.typemap)
    require(fname == "slice" and mod_name in ("__builtin__", "builtins"))
    require(len(index_def.args) == 2)
    lhs = index_def.args[0]
    rhs = index_def.args[1]
    size_typ = self.typemap[dsize.name]
    lhs_typ = self.typemap[lhs.name]
    rhs_typ = self.typemap[rhs.name]

    if config.DEBUG_ARRAY_OPT >= 2:
        print(
            "slice_size",
            "index=",
            index,
            "dsize=",
            dsize,
            "index_def=",
            index_def,
            "lhs=",
            lhs,
            "rhs=",
            rhs,
            "size_typ=",
            size_typ,
            "lhs_typ=",
            lhs_typ,
            "rhs_typ=",
            rhs_typ,
        )

    # Make a deepcopy of the original slice to use as the
    # replacement slice, which we will modify as necessary
    # below to convert all negative constants in the slice
    # to be relative to the dimension size.
    replacement_slice = copy.deepcopy(index_def)
    need_replacement = False

    # Fill in the left side of the slice's ":" with 0 if it wasn't specified.
    if isinstance(lhs_typ, types.NoneType):
        zero_var = ir.Var(scope, mk_unique_var("zero"), loc)
        zero = ir.Const(0, loc)
        stmts.append(ir.Assign(value=zero, target=zero_var, loc=loc))
        self._define(equiv_set, zero_var, types.IntegerLiteral(0), zero)
        lhs = zero_var
        lhs_typ = types.IntegerLiteral(0)
        replacement_slice.args = (lhs, replacement_slice.args[1])
        need_replacement = True
        if config.DEBUG_ARRAY_OPT >= 2:
            print("Replacing slice because lhs is None.")

    # Fill in the right side of the slice's ":" with the array
    # length if it wasn't specified.
    if isinstance(rhs_typ, types.NoneType):
        rhs = dsize
        rhs_typ = size_typ
        replacement_slice.args = (replacement_slice.args[0], rhs)
        need_replacement = True
        if config.DEBUG_ARRAY_OPT >= 2:
            print("Replacing slice because lhs is None.")

    lhs_rel = equiv_set.get_rel(lhs)
    rhs_rel = equiv_set.get_rel(rhs)
    dsize_rel = equiv_set.get_rel(dsize)
    if config.DEBUG_ARRAY_OPT >= 2:
        print("lhs_rel", lhs_rel, "rhs_rel", rhs_rel, "dsize_rel", dsize_rel)

    # Update replacement slice with the real index value if we can
    # compute it at compile time.
    lhs, lhs_typ, lhs_rel, replacement_slice, need_replacement, lhs_known = (
        self.update_replacement_slice(
            lhs,
            lhs_typ,
            lhs_rel,
            dsize_rel,
            replacement_slice,
            0,
            need_replacement,
            loc,
            scope,
            stmts,
            equiv_set,
            size_typ,
            dsize,
        )
    )
    rhs, rhs_typ, rhs_rel, replacement_slice, need_replacement, rhs_known = (
        self.update_replacement_slice(
            rhs,
            rhs_typ,
            rhs_rel,
            dsize_rel,
            replacement_slice,
            1,
            need_replacement,
            loc,
            scope,
            stmts,
            equiv_set,
            size_typ,
            dsize,
        )
    )
    if config.DEBUG_ARRAY_OPT >= 2:
        print("lhs_known:", lhs_known)
        print("rhs_known:", rhs_known)

    # If neither of the parts of the slice were negative constants
    # then we don't need to do slice replacement in the IR.
    if not need_replacement:
        replacement_slice_var = None
    else:
        # Create a new var for the replacement slice.
        replacement_slice_var = ir.Var(scope, mk_unique_var("replacement_slice"), loc)
        # Create a deepcopy of slice calltype so that when we change it below
        # the original isn't changed.  Make the types of the parts of the slice
        # intp.
        new_arg_typs = (types.intp, types.intp)
        rs_calltype = self.typemap[index_def.func.name].get_call_type(
            self.context, new_arg_typs, {}
        )
        self.calltypes[replacement_slice] = rs_calltype
        stmts.append(
            ir.Assign(value=replacement_slice, target=replacement_slice_var, loc=loc)
        )
        # The type of the replacement slice is the same type as the original.
        self.typemap[replacement_slice_var.name] = self.typemap[index.name]

    if config.DEBUG_ARRAY_OPT >= 2:
        print("after rewriting negatives", "lhs_rel", lhs_rel, "rhs_rel", rhs_rel)

    if lhs_known and rhs_known:
        if config.DEBUG_ARRAY_OPT >= 2:
            print("lhs and rhs known so return static size")
        return self.gen_static_slice_size(
            lhs_rel, rhs_rel, loc, scope, stmts, equiv_set
        ), replacement_slice_var

    if (
        lhs_rel == 0
        and isinstance(rhs_rel, tuple)
        and equiv_set.is_equiv(dsize, rhs_rel[0])
        and rhs_rel[1] == 0
    ):
        return dsize, None

    slice_typ = types.intp
    orig_slice_typ = slice_typ

    size_var = ir.Var(scope, mk_unique_var("slice_size"), loc)
    size_val = ir.Expr.binop(operator.sub, rhs, lhs, loc=loc)
    self.calltypes[size_val] = signature(slice_typ, rhs_typ, lhs_typ)
    self._define(equiv_set, size_var, slice_typ, size_val)
    size_rel = equiv_set.get_rel(size_var)
    if config.DEBUG_ARRAY_OPT >= 2:
        print("size_rel", size_rel, type(size_rel))

    wrap_var = ir.Var(scope, mk_unique_var("wrap"), loc)
    wrap_def = ir.Global("wrap_index", wrap_index, loc=loc)
    fnty = get_global_func_typ(wrap_index)
    sig = self.context.resolve_function_type(
        fnty,
        (
            orig_slice_typ,
            size_typ,
        ),
        {},
    )
    self._define(equiv_set, wrap_var, fnty, wrap_def)

    def gen_wrap_if_not_known(val, val_typ, known):
        if not known:
            var = ir.Var(scope, mk_unique_var("var"), loc)
            var_typ = types.intp
            new_value = ir.Expr.call(wrap_var, [val, dsize], {}, loc)
            self._define(equiv_set, var, var_typ, new_value)
            self.calltypes[new_value] = sig
            return (var, var_typ, new_value)
        else:
            return (val, val_typ, None)

    var1, var1_typ, value1 = gen_wrap_if_not_known(lhs, lhs_typ, lhs_known)
    var2, var2_typ, value2 = gen_wrap_if_not_known(rhs, rhs_typ, rhs_known)

    post_wrap_size_var = ir.Var(scope, mk_unique_var("post_wrap_slice_size"), loc)
    post_wrap_size_val = ir.Expr.binop(operator.sub, var2, var1, loc=loc)
    self.calltypes[post_wrap_size_val] = signature(slice_typ, var2_typ, var1_typ)
    self._define(equiv_set, post_wrap_size_var, slice_typ, post_wrap_size_val)

    stmts.append(ir.Assign(value=size_val, target=size_var, loc=loc))
    stmts.append(ir.Assign(value=wrap_def, target=wrap_var, loc=loc))
    if value1 is not None:
        stmts.append(ir.Assign(value=value1, target=var1, loc=loc))
    if value2 is not None:
        stmts.append(ir.Assign(value=value2, target=var2, loc=loc))
    stmts.append(
        ir.Assign(value=post_wrap_size_val, target=post_wrap_size_var, loc=loc)
    )

    # rel_map keeps a map of relative sizes that we have seen so
    # that if we compute the same relative sizes different times
    # in different ways we can associate those two instances
    # of the same relative size to the same equivalence class.
    if isinstance(size_rel, tuple):
        if config.DEBUG_ARRAY_OPT >= 2:
            print("size_rel is tuple", equiv_set.rel_map)
        rel_map_entry = None
        for rme, rme_tuple in equiv_set.rel_map.items():
            if rme[1] == size_rel[1] and equiv_set.is_equiv(rme[0], size_rel[0]):
                rel_map_entry = rme_tuple
                break

        if rel_map_entry is not None:
            # We have seen this relative size before so establish
            # equivalence to the previous variable.
            if config.DEBUG_ARRAY_OPT >= 2:
                print("establishing equivalence to", rel_map_entry)
            equiv_set.insert_equiv(size_var, rel_map_entry[0])
            equiv_set.insert_equiv(post_wrap_size_var, rel_map_entry[1])
        else:
            # The first time we've seen this relative size so
            # remember the variable defining that size.
            equiv_set.rel_map[size_rel] = (size_var, post_wrap_size_var)

    return post_wrap_size_var, replacement_slice_var