# Buggy code
def _arrayexpr_tree_to_ir(
    func_ir,
    typingctx,
    typemap,
    calltypes,
    equiv_set,
    init_block,
    expr_out_var,
    expr,
    parfor_index_tuple_var,
    all_parfor_indices,
    avail_vars,
):
    """generate IR from array_expr's expr tree recursively. Assign output to
    expr_out_var and returns the whole IR as a list of Assign nodes.
    """
    el_typ = typemap[expr_out_var.name]
    scope = expr_out_var.scope
    loc = expr_out_var.loc
    out_ir = []

    if isinstance(expr, tuple):
        op, arr_expr_args = expr
        arg_vars = []
        for arg in arr_expr_args:
            arg_out_var = ir.Var(scope, mk_unique_var("$arg_out_var"), loc)
            typemap[arg_out_var.name] = el_typ
            out_ir += _arrayexpr_tree_to_ir(
                func_ir,
                typingctx,
                typemap,
                calltypes,
                equiv_set,
                init_block,
                arg_out_var,
                arg,
                parfor_index_tuple_var,
                all_parfor_indices,
                avail_vars,
            )
            arg_vars.append(arg_out_var)
        if op in npydecl.supported_array_operators:
            el_typ1 = typemap[arg_vars[0].name]
            if len(arg_vars) == 2:
                el_typ2 = typemap[arg_vars[1].name]
                func_typ = typingctx.resolve_function_type(op, (el_typ1, el_typ), {})
                ir_expr = ir.Expr.binop(op, arg_vars[0], arg_vars[1], loc)
                if op == operator.truediv:
                    func_typ, ir_expr = _gen_np_divide(
                        arg_vars[0], arg_vars[1], out_ir, typemap
                    )
            else:
                func_typ = typingctx.resolve_function_type(op, (el_typ1,), {})
                ir_expr = ir.Expr.unary(op, arg_vars[0], loc)
            calltypes[ir_expr] = func_typ
            el_typ = func_typ.return_type
            out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
        for T in array_analysis.MAP_TYPES:
            if isinstance(op, T):
                # elif isinstance(op, (np.ufunc, DUFunc)):
                # function calls are stored in variables which are not removed
                # op is typing_key to the variables type
                func_var_name = _find_func_var(typemap, op, avail_vars, loc=loc)
                func_var = ir.Var(scope, mk_unique_var(func_var_name), loc)
                typemap[func_var.name] = typemap[func_var_name]
                func_var_def = copy.deepcopy(func_ir.get_definition(func_var_name))
                if (
                    isinstance(func_var_def, ir.Expr)
                    and func_var_def.op == "getattr"
                    and func_var_def.attr == "sqrt"
                ):
                    g_math_var = ir.Var(scope, mk_unique_var("$math_g_var"), loc)
                    typemap[g_math_var.name] = types.misc.Module(math)
                    g_math = ir.Global("math", math, loc)
                    g_math_assign = ir.Assign(g_math, g_math_var, loc)
                    func_var_def = ir.Expr.getattr(g_math_var, "sqrt", loc)
                    out_ir.append(g_math_assign)
                #                     out_ir.append(func_var_def)
                ir_expr = ir.Expr.call(func_var, arg_vars, (), loc)
                call_typ = typemap[func_var.name].get_call_type(
                    typingctx, tuple(typemap[a.name] for a in arg_vars), {}
                )
                calltypes[ir_expr] = call_typ
                el_typ = call_typ.return_type
                # signature(el_typ, el_typ)
                out_ir.append(ir.Assign(func_var_def, func_var, loc))
                out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
    elif isinstance(expr, ir.Var):
        var_typ = typemap[expr.name]
        if isinstance(var_typ, types.Array):
            el_typ = var_typ.dtype
            ir_expr = _gen_arrayexpr_getitem(
                equiv_set,
                expr,
                parfor_index_tuple_var,
                all_parfor_indices,
                el_typ,
                calltypes,
                typingctx,
                typemap,
                init_block,
                out_ir,
            )
        else:
            # assert typemap[expr.name]==el_typ
            el_typ = var_typ
            ir_expr = expr
        out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
    elif isinstance(expr, ir.Const):
        el_typ = typing.Context().resolve_value_type(expr.value)
        out_ir.append(ir.Assign(expr, expr_out_var, loc))

    if len(out_ir) == 0:
        raise errors.UnsupportedRewriteError(
            f"Don't know how to translate array expression '{expr:r}'",
            loc=expr.loc,
        )
    typemap.pop(expr_out_var.name, None)
    typemap[expr_out_var.name] = el_typ
    return out_ir


# Traceback (numba.core.errors.TypingError)
(SDC_MASTER) C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\num
ba>python -W ignore .\example_script.py
DEBUG: typing operator_add: lhs=StringArrayType(), rhs=StringArrayType()
DEBUG: typing operator_add: lhs=StringArrayType(), rhs=bool
DEBUG: typing: str_arr_operator_eq_overload: left, right StringArrayType() bool
Traceback (most recent call last):
File ".\example_script.py", line 98, in <module>
print(foo(a1, a2))
File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\core\dispatcher.py", line 401, in _compile_for_args
error_rewrite(e, 'typing')
File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\core\dispatcher.py", line 344, in error_rewrite
reraise(type(e), e, None)
File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\core\utils.py", line 79, in reraise
raise value.with_traceback(tb)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: convert t
o parfors)
Invalid use of Function(<built-in function eq>) with argument(s) of type(s): (St
ringArrayType(), bool)
Known signatures:
* (bool, bool) -> bool
* (int8, int8) -> bool
* (int16, int16) -> bool
* (int32, int32) -> bool
* (int64, int64) -> bool
* (uint8, uint8) -> bool
* (uint16, uint16) -> bool
* (uint32, uint32) -> bool
* (uint64, uint64) -> bool
* (float32, float32) -> bool
* (float64, float64) -> bool
* (complex64, complex64) -> bool
* (complex128, complex128) -> bool
* parameterized
In definition 0:
All templates rejected with literals.
...
All templates rejected with literals.
In definition 35:
All templates rejected without literals.
This error is usually caused by passing an argument of a type that is unsupported by the named function.

# Fixed code
def _arrayexpr_tree_to_ir(
    func_ir,
    typingctx,
    typemap,
    calltypes,
    equiv_set,
    init_block,
    expr_out_var,
    expr,
    parfor_index_tuple_var,
    all_parfor_indices,
    avail_vars,
):
    """generate IR from array_expr's expr tree recursively. Assign output to
    expr_out_var and returns the whole IR as a list of Assign nodes.
    """
    el_typ = typemap[expr_out_var.name]
    scope = expr_out_var.scope
    loc = expr_out_var.loc
    out_ir = []

    if isinstance(expr, tuple):
        op, arr_expr_args = expr
        arg_vars = []
        for arg in arr_expr_args:
            arg_out_var = ir.Var(scope, mk_unique_var("$arg_out_var"), loc)
            typemap[arg_out_var.name] = el_typ
            out_ir += _arrayexpr_tree_to_ir(
                func_ir,
                typingctx,
                typemap,
                calltypes,
                equiv_set,
                init_block,
                arg_out_var,
                arg,
                parfor_index_tuple_var,
                all_parfor_indices,
                avail_vars,
            )
            arg_vars.append(arg_out_var)
        if op in npydecl.supported_array_operators:
            el_typ1 = typemap[arg_vars[0].name]
            if len(arg_vars) == 2:
                el_typ2 = typemap[arg_vars[1].name]
                func_typ = typingctx.resolve_function_type(op, (el_typ1, el_typ2), {})
                ir_expr = ir.Expr.binop(op, arg_vars[0], arg_vars[1], loc)
                if op == operator.truediv:
                    func_typ, ir_expr = _gen_np_divide(
                        arg_vars[0], arg_vars[1], out_ir, typemap
                    )
            else:
                func_typ = typingctx.resolve_function_type(op, (el_typ1,), {})
                ir_expr = ir.Expr.unary(op, arg_vars[0], loc)
            calltypes[ir_expr] = func_typ
            el_typ = func_typ.return_type
            out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
        for T in array_analysis.MAP_TYPES:
            if isinstance(op, T):
                # elif isinstance(op, (np.ufunc, DUFunc)):
                # function calls are stored in variables which are not removed
                # op is typing_key to the variables type
                func_var_name = _find_func_var(typemap, op, avail_vars, loc=loc)
                func_var = ir.Var(scope, mk_unique_var(func_var_name), loc)
                typemap[func_var.name] = typemap[func_var_name]
                func_var_def = copy.deepcopy(func_ir.get_definition(func_var_name))
                if (
                    isinstance(func_var_def, ir.Expr)
                    and func_var_def.op == "getattr"
                    and func_var_def.attr == "sqrt"
                ):
                    g_math_var = ir.Var(scope, mk_unique_var("$math_g_var"), loc)
                    typemap[g_math_var.name] = types.misc.Module(math)
                    g_math = ir.Global("math", math, loc)
                    g_math_assign = ir.Assign(g_math, g_math_var, loc)
                    func_var_def = ir.Expr.getattr(g_math_var, "sqrt", loc)
                    out_ir.append(g_math_assign)
                #                     out_ir.append(func_var_def)
                ir_expr = ir.Expr.call(func_var, arg_vars, (), loc)
                call_typ = typemap[func_var.name].get_call_type(
                    typingctx, tuple(typemap[a.name] for a in arg_vars), {}
                )
                calltypes[ir_expr] = call_typ
                el_typ = call_typ.return_type
                # signature(el_typ, el_typ)
                out_ir.append(ir.Assign(func_var_def, func_var, loc))
                out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
    elif isinstance(expr, ir.Var):
        var_typ = typemap[expr.name]
        if isinstance(var_typ, types.Array):
            el_typ = var_typ.dtype
            ir_expr = _gen_arrayexpr_getitem(
                equiv_set,
                expr,
                parfor_index_tuple_var,
                all_parfor_indices,
                el_typ,
                calltypes,
                typingctx,
                typemap,
                init_block,
                out_ir,
            )
        else:
            # assert typemap[expr.name]==el_typ
            el_typ = var_typ
            ir_expr = expr
        out_ir.append(ir.Assign(ir_expr, expr_out_var, loc))
    elif isinstance(expr, ir.Const):
        el_typ = typing.Context().resolve_value_type(expr.value)
        out_ir.append(ir.Assign(expr, expr_out_var, loc))

    if len(out_ir) == 0:
        raise errors.UnsupportedRewriteError(
            f"Don't know how to translate array expression '{expr:r}'",
            loc=expr.loc,
        )
    typemap.pop(expr_out_var.name, None)
    typemap[expr_out_var.name] = el_typ
    return out_ir