# Buggy code
def make_quicksort_impl(wrap, lt=None, is_argsort=False):
    intp = types.intp
    zero = intp(0)

    # Two subroutines to make the core algorithm generic wrt. argsort
    # or normal sorting.  Note the genericity may make basic sort()
    # slightly slower (~5%)
    if is_argsort:

        @wrap
        def make_res(A):
            return np.arange(A.size)

        @wrap
        def GET(A, idx_or_val):
            return A[idx_or_val]

    else:

        @wrap
        def make_res(A):
            return A

        @wrap
        def GET(A, idx_or_val):
            return idx_or_val

    def default_lt(a, b):
        """
        Trivial comparison function between two keys.
        """
        return a < b

    LT = wrap(lt if lt is not None else default_lt)

    @wrap
    def insertion_sort(A, R, low, high):
        """
        Insertion sort A[low:high + 1]. Note the inclusive bounds.
        """
        assert low >= 0
        if high <= low:
            return

        for i in range(low + 1, high + 1):
            k = R[i]
            v = GET(A, k)
            # Insert v into A[low:i]
            j = i
            while j > low and LT(v, GET(A, R[j - 1])):
                # Make place for moving A[i] downwards
                R[j] = R[j - 1]
                j -= 1
            R[j] = k

    @wrap
    def partition(A, R, low, high):
        """
        Partition A[low:high + 1] around a chosen pivot.  The pivot's index
        is returned.
        """
        assert low >= 0
        assert high > low

        mid = (low + high) >> 1
        # NOTE: the pattern of swaps below for the pivot choice and the
        # partitioning gives good results (i.e. regular O(n log n))
        # on sorted, reverse-sorted, and uniform arrays.  Subtle changes
        # risk breaking this property.

        # median of three {low, middle, high}
        if LT(GET(A, R[mid]), GET(A, R[low])):
            R[low], R[mid] = R[mid], R[low]
        if LT(GET(A, R[high]), GET(A, R[mid])):
            R[high], R[mid] = R[mid], R[high]
        if LT(GET(A, R[mid]), GET(A, R[low])):
            R[low], R[mid] = R[mid], R[low]
        pivot = GET(A, R[mid])

        # Temporarily stash the pivot at the end
        R[high], R[mid] = R[mid], R[high]
        i = low
        j = high - 1
        while True:
            while i < high and LT(GET(A, R[i]), pivot):
                i += 1
            while j >= low and LT(pivot, GET(A, R[j])):
                j -= 1
            if i >= j:
                break
            R[i], R[j] = R[j], R[i]
            i += 1
            j -= 1
        # Put the pivot back in its final place (all items before `i`
        # are smaller than the pivot, all items at/after `i` are larger)
        R[i], R[high] = R[high], R[i]
        return i

    @wrap
    def partition3(A, low, high):
        """
        Three-way partition [low, high) around a chosen pivot.
        A tuple (lt, gt) is returned such that:
            - all elements in [low, lt) are < pivot
            - all elements in [lt, gt] are == pivot
            - all elements in (gt, high] are > pivot
        """
        mid = (low + high) >> 1
        # median of three {low, middle, high}
        if LT(A[mid], A[low]):
            A[low], A[mid] = A[mid], A[low]
        if LT(A[high], A[mid]):
            A[high], A[mid] = A[mid], A[high]
        if LT(A[mid], A[low]):
            A[low], A[mid] = A[mid], A[low]
        pivot = A[mid]

        A[low], A[mid] = A[mid], A[low]
        lt = low
        gt = high
        i = low + 1
        while i <= gt:
            if LT(A[i], pivot):
                A[lt], A[i] = A[i], A[lt]
                lt += 1
                i += 1
            elif LT(pivot, A[i]):
                A[gt], A[i] = A[i], A[gt]
                gt -= 1
            else:
                i += 1
        return lt, gt

    @wrap
    def run_quicksort(A):
        R = make_res(A)

        if len(A) < 2:
            return R

        stack = [Partition(zero, zero)] * MAX_STACK
        stack[0] = Partition(zero, len(A) - 1)
        n = 1

        while n > 0:
            n -= 1
            low, high = stack[n]
            # Partition until it becomes more efficient to do an insertion sort
            while high - low >= SMALL_QUICKSORT:
                assert n < MAX_STACK
                i = partition(A, R, low, high)
                # Push largest partition on the stack
                if high - i > i - low:
                    # Right is larger
                    if high > i:
                        stack[n] = Partition(i + 1, high)
                        n += 1
                    high = i - 1
                else:
                    if i > low:
                        stack[n] = Partition(low, i - 1)
                        n += 1
                    low = i + 1

            insertion_sort(A, R, low, high)

        return R

    # Unused quicksort implementation based on 3-way partitioning; the
    # partitioning scheme turns out exhibiting bad behaviour on sorted arrays.
    @wrap
    def _run_quicksort(A):
        stack = [Partition(zero, zero)] * 100
        stack[0] = Partition(zero, len(A) - 1)
        n = 1

        while n > 0:
            n -= 1
            low, high = stack[n]
            # Partition until it becomes more efficient to do an insertion sort
            while high - low >= SMALL_QUICKSORT:
                assert n < MAX_STACK
                l, r = partition3(A, low, high)
                # One trivial (empty) partition => iterate on the other
                if r == high:
                    high = l - 1
                elif l == low:
                    low = r + 1
                # Push largest partition on the stack
                elif high - r > l - low:
                    # Right is larger
                    stack[n] = Partition(r + 1, high)
                    n += 1
                    high = l - 1
                else:
                    stack[n] = Partition(low, l - 1)
                    n += 1
                    low = r + 1

            insertion_sort(A, low, high)

    return QuicksortImplementation(
        wrap, partition, partition3, insertion_sort, run_quicksort
    )


# 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 make_quicksort_impl(wrap, lt=None, is_argsort=False, is_list=False):
    intp = types.intp
    zero = intp(0)

    # Two subroutines to make the core algorithm generic wrt. argsort
    # or normal sorting.  Note the genericity may make basic sort()
    # slightly slower (~5%)
    if is_argsort:
        if is_list:

            @wrap
            def make_res(A):
                return [x for x in range(len(A))]
        else:

            @wrap
            def make_res(A):
                return np.arange(A.size)

        @wrap
        def GET(A, idx_or_val):
            return A[idx_or_val]

    else:

        @wrap
        def make_res(A):
            return A

        @wrap
        def GET(A, idx_or_val):
            return idx_or_val

    def default_lt(a, b):
        """
        Trivial comparison function between two keys.
        """
        return a < b

    LT = wrap(lt if lt is not None else default_lt)

    @wrap
    def insertion_sort(A, R, low, high):
        """
        Insertion sort A[low:high + 1]. Note the inclusive bounds.
        """
        assert low >= 0
        if high <= low:
            return

        for i in range(low + 1, high + 1):
            k = R[i]
            v = GET(A, k)
            # Insert v into A[low:i]
            j = i
            while j > low and LT(v, GET(A, R[j - 1])):
                # Make place for moving A[i] downwards
                R[j] = R[j - 1]
                j -= 1
            R[j] = k

    @wrap
    def partition(A, R, low, high):
        """
        Partition A[low:high + 1] around a chosen pivot.  The pivot's index
        is returned.
        """
        assert low >= 0
        assert high > low

        mid = (low + high) >> 1
        # NOTE: the pattern of swaps below for the pivot choice and the
        # partitioning gives good results (i.e. regular O(n log n))
        # on sorted, reverse-sorted, and uniform arrays.  Subtle changes
        # risk breaking this property.

        # median of three {low, middle, high}
        if LT(GET(A, R[mid]), GET(A, R[low])):
            R[low], R[mid] = R[mid], R[low]
        if LT(GET(A, R[high]), GET(A, R[mid])):
            R[high], R[mid] = R[mid], R[high]
        if LT(GET(A, R[mid]), GET(A, R[low])):
            R[low], R[mid] = R[mid], R[low]
        pivot = GET(A, R[mid])

        # Temporarily stash the pivot at the end
        R[high], R[mid] = R[mid], R[high]
        i = low
        j = high - 1
        while True:
            while i < high and LT(GET(A, R[i]), pivot):
                i += 1
            while j >= low and LT(pivot, GET(A, R[j])):
                j -= 1
            if i >= j:
                break
            R[i], R[j] = R[j], R[i]
            i += 1
            j -= 1
        # Put the pivot back in its final place (all items before `i`
        # are smaller than the pivot, all items at/after `i` are larger)
        R[i], R[high] = R[high], R[i]
        return i

    @wrap
    def partition3(A, low, high):
        """
        Three-way partition [low, high) around a chosen pivot.
        A tuple (lt, gt) is returned such that:
            - all elements in [low, lt) are < pivot
            - all elements in [lt, gt] are == pivot
            - all elements in (gt, high] are > pivot
        """
        mid = (low + high) >> 1
        # median of three {low, middle, high}
        if LT(A[mid], A[low]):
            A[low], A[mid] = A[mid], A[low]
        if LT(A[high], A[mid]):
            A[high], A[mid] = A[mid], A[high]
        if LT(A[mid], A[low]):
            A[low], A[mid] = A[mid], A[low]
        pivot = A[mid]

        A[low], A[mid] = A[mid], A[low]
        lt = low
        gt = high
        i = low + 1
        while i <= gt:
            if LT(A[i], pivot):
                A[lt], A[i] = A[i], A[lt]
                lt += 1
                i += 1
            elif LT(pivot, A[i]):
                A[gt], A[i] = A[i], A[gt]
                gt -= 1
            else:
                i += 1
        return lt, gt

    @wrap
    def run_quicksort(A):
        R = make_res(A)

        if len(A) < 2:
            return R

        stack = [Partition(zero, zero)] * MAX_STACK
        stack[0] = Partition(zero, len(A) - 1)
        n = 1

        while n > 0:
            n -= 1
            low, high = stack[n]
            # Partition until it becomes more efficient to do an insertion sort
            while high - low >= SMALL_QUICKSORT:
                assert n < MAX_STACK
                i = partition(A, R, low, high)
                # Push largest partition on the stack
                if high - i > i - low:
                    # Right is larger
                    if high > i:
                        stack[n] = Partition(i + 1, high)
                        n += 1
                    high = i - 1
                else:
                    if i > low:
                        stack[n] = Partition(low, i - 1)
                        n += 1
                    low = i + 1

            insertion_sort(A, R, low, high)

        return R

    # Unused quicksort implementation based on 3-way partitioning; the
    # partitioning scheme turns out exhibiting bad behaviour on sorted arrays.
    @wrap
    def _run_quicksort(A):
        stack = [Partition(zero, zero)] * 100
        stack[0] = Partition(zero, len(A) - 1)
        n = 1

        while n > 0:
            n -= 1
            low, high = stack[n]
            # Partition until it becomes more efficient to do an insertion sort
            while high - low >= SMALL_QUICKSORT:
                assert n < MAX_STACK
                l, r = partition3(A, low, high)
                # One trivial (empty) partition => iterate on the other
                if r == high:
                    high = l - 1
                elif l == low:
                    low = r + 1
                # Push largest partition on the stack
                elif high - r > l - low:
                    # Right is larger
                    stack[n] = Partition(r + 1, high)
                    n += 1
                    high = l - 1
                else:
                    stack[n] = Partition(low, l - 1)
                    n += 1
                    low = r + 1

            insertion_sort(A, low, high)

    return QuicksortImplementation(
        wrap, partition, partition3, insertion_sort, run_quicksort
    )