Repository: numba/numba

Issue:
A typo affecting binop typeinfer in parfor's _arrayexpr_tree_to_ir
## Reporting a bug

- [x] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

Hi, 

While investigating issue caused by #5157 I observed strange behavior when overloaded operator.eq for user type was reentered with wrong type of second argument resulting in completely unrelated compilation failure message, like:
```

  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\parfor.py", line 2218, in _arrayexpr_to_parfor
    _arrayexpr_tree_to_ir(
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\parfor.py", line 2662, in _arrayexpr_tree_to_ir
    func_typ = typingctx.resolve_function_type(op, (el_typ1, el_typ), {})   # FI
XME
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\typing\context.py", line 199, in resolve_function_type
    res = self._resolve_user_function_type(func, args, kws)
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\typing\context.py", line 251, in _resolve_user_function_type
    return func.get_call_type(self, args, kws)
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\types\functions.py", line 150, in get_call_type
    failures.raise_error()
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\types\functions.py", line 79, in raise_error
    raise errors.TypingError(self.format())
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\types\functions.py", line 44, in format
    explain = self._context.explain_function_type(self._function_type)
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\typing\context.py", line 170, in explain_function_type
    sigs, param = func.get_call_signatures()
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\numba_master\numba\nu
mba\types\functions.py", line 156, in get_call_signatures
    sigs += getattr(temp, 'cases', [])
TypeError: Failed in nopython mode pipeline (step: convert to parfors)
'Signature' object is not iterable
```

I was able to find that it was caused by a typo in parfor.py _arrayexpr_tree_to_ir() which calls resolve_function_type() with wrong parameters, i.e.
```
def _arrayexpr_tree_to_ir(
    ...
        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), {})   # <---- triggers typeinfer with operation result type (bool) as a second argument type
                ir_expr = ir.Expr.binop(op, arg_vars[0], arg_vars[1], loc)
```

It can be reproduced on master with the following script (but the resulting error is now different and correctly refers to wrong types used with operator.eq):
```
import numba
from numba.extending import (
    overload,
    models,
    lower_builtin,
    register_model,
    make_attribute_wrapper,
    type_callable,
    typeof_impl,
)
from numba import types, cgutils
from numba import njit
import operator
import numpy as np


class StringArray(object):
    def __init__(self, str_list):
        # dummy constructor
        self.data = str_list

    def __repr__(self):
        return 'StringArray({})'.format(self.data)

class StringArrayType(types.Type):
    def __init__(self):
        super(StringArrayType, self).__init__(
            name='StringArrayType()')

    @property
    def dtype(self):
        return types.unicode_type

string_array_type = StringArrayType()
str_arr_data_type = types.containers.List(types.unicode_type)

@typeof_impl.register(StringArray)
def typeof_string_array(val, c):
    return string_array_type

@type_callable(StringArray)
def type_string_array_call(context):
    def typer(str_list):
        return string_array_type
    return typer

@register_model(StringArrayType)
class StringArrayModel(models.StructModel):
    def __init__(self, dmm, fe_type):

        members = [
            ('data', str_arr_data_type),
        ]
        models.StructModel.__init__(self, dmm, fe_type, members)

make_attribute_wrapper(StringArrayType, 'data', '_data')


@lower_builtin(StringArray, str_arr_data_type)
def impl_dummy(context, builder, sig, args):
    data_val, = args
    str_arr = cgutils.create_struct_proxy(
        sig.return_type)(context, builder)
    str_arr.data = data_val

    # increase refcount of stored values
    if context.enable_nrt:
        context.nrt.incref(builder, sig.args[0], data_val)

    return str_arr._getvalue()


@overload(operator.eq)
def str_arr_operator_eq_ovld(lhs, rhs):
    print("DEBUG: typing operator_add: lhs={}, rhs={}".format(lhs, rhs))
    if isinstance(lhs, StringArrayType) and isinstance(rhs, StringArrayType):

        def impl(lhs, rhs):
            n = len(lhs._data)
            res = np.empty(n, dtype=types.bool_)
            for i in numba.prange(n):
                res[i] = lhs._data[i] == rhs._data[i]
            return res

        return impl


@njit(parallel=True)
def foo(a, b):

    left = StringArray(a)
    right = StringArray(b)
    return left == right


a1 = ['a', 'b', 'c']
a2 = ['a', 'b', 'c']
print(foo(a1, a2))

```
gives:
```
(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.
```

So since the issues is still applicable on master, it's probably worth fixing.

Numba version: 0.50.0.dev0+72.g296250b61

Best Regards,
Alexey.


Discussion:
Thanks for the report. I can reproduce. Looks like it ought to be something like:
```
func_typ = typingctx.resolve_function_type(op, (el_typ1, el_typ2), {}) 
```
? i.e. resolve on the types of the two args. Applying that locally gives me a lowering error `No definition for lowering <class '__main__.StringArray'>(reflected list(unicode_type),) -> StringArrayType()` which seems reasonable.
@stuartarchibald  Yes, exactly, that will solve the issue. I initially wanted to reproduce the problem of #5157 with the script, but missed that I have to use 'reflected=True' in _str_arr_data_type_ definition. But anyway the script should fail to compile with the fix too and it's not really important what first error message is overwritten.
@kozlov-alexey I see, thanks. I think that given this is evidently incorrect, I'm inclined to go with a test that asserts that such a case doesn't fail with `Invalid use of Function(<built-in function eq>) with argument(s) of type(s): (StringArrayType(), bool)`, i.e. invalid lookup, and that'd be fine. Will get a patch in shortly.

Resolving patch:
diff --git a/numba/parfors/parfor.py b/numba/parfors/parfor.py
--- a/numba/parfors/parfor.py
+++ b/numba/parfors/parfor.py
@@ -2864,7 +2864,8 @@ def _arrayexpr_tree_to_ir(
             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), {})
+                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(


Test patch: