Repository: numba/numba

Issue:
overload_method error on typing of literals
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [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).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->

Looks like the type inference only passes the literal types to `overload_method`, and doesn't convert to generic types and try again. The example below prints `foo.g(a) input type Literal[str](TEST)` and fails in typing.

```python
import numba
from numba.extending import type_callable, overload_method
from numba import types
from numba.extending import models, register_model

class Foo:
    def __init__(self):
        pass
    def g(self, a):
        return a

class FooType(types.Type):
    def __init__(self):
        super(FooType, self).__init__(name='Foo')

register_model(FooType)(models.OpaqueModel)
foo_type = FooType()

@type_callable(Foo)
def type_foo(context):
    def typer():
        return foo_type
    return typer

@overload_method(FooType, 'g')
def g_overload(f_t, a_t):
    if a_t == types.unicode_type:
        return lambda f, a: a
    print("foo.g(a) input type", a_t)

@numba.njit
def test():
    foo = Foo()
    return foo.g('TEST')

test()
```



Discussion:
Looks like `IntegerLiteral` inherits from `Integer` but `StringLiteral` doesn't inherit from `UnicodeType`. Which one is correct?

Resolving patch:
diff --git a/numba/targets/callconv.py b/numba/targets/callconv.py
--- a/numba/targets/callconv.py
+++ b/numba/targets/callconv.py
@@ -148,13 +148,6 @@ def decode_arguments(self, builder, argtypes, func):
         arginfo = self._get_arg_packer(argtypes)
         return arginfo.from_arguments(builder, raw_args)
 
-    def _fix_argtypes(self, argtypes):
-        """
-        Fix argument types, removing any omitted arguments.
-        """
-        return tuple(ty for ty in argtypes
-                     if not isinstance(ty, types.Omitted))
-
     def _get_arg_packer(self, argtypes):
         """
         Get an argument packer for the given argument types.
diff --git a/numba/typing/templates.py b/numba/typing/templates.py
--- a/numba/typing/templates.py
+++ b/numba/typing/templates.py
@@ -86,6 +86,13 @@ def as_method(self):
             return self
         sig = signature(self.return_type, *self.args[1:],
                         recvr=self.args[0])
+
+        # Adjust the python signature
+        params = list(self.pysig.parameters.values())[1:]
+        sig.pysig = utils.pySignature(
+            parameters=params,
+            return_annotation=self.pysig.return_annotation,
+        )
         return sig
 
     def as_function(self):
@@ -568,7 +575,7 @@ def _get_dispatcher(cls, context, typ, attr, sig_args, sig_kws):
         Get the compiled dispatcher implementing the attribute for
         the given formal signature.
         """
-        cache_key = context, typ, attr
+        cache_key = context, typ, attr, tuple(sig_args), tuple(sig_kws.items())
         try:
             disp = cls._impl_cache[cache_key]
         except KeyError:
diff --git a/numba/utils.py b/numba/utils.py
--- a/numba/utils.py
+++ b/numba/utils.py
@@ -78,10 +78,12 @@ def erase_traceback(exc_value):
 
 try:
     from inspect import signature as pysignature
+    from inspect import Signature as pySignature
     from inspect import Parameter as pyParameter
 except ImportError:
     try:
         from funcsigs import signature as pysignature
+        from funcsigs import Signature as pySignature
         from funcsigs import Parameter as pyParameter
     except ImportError:
         raise ImportError("please install the 'funcsigs' package "


Test patch:
diff --git a/numba/tests/test_extending.py b/numba/tests/test_extending.py
--- a/numba/tests/test_extending.py
+++ b/numba/tests/test_extending.py
@@ -12,7 +12,7 @@
 import numpy as np
 
 from numba import unittest_support as unittest
-from numba import jit, types, errors, typing, compiler
+from numba import njit, jit, types, errors, typing, compiler
 from numba.targets.registry import cpu_target
 from numba.compiler import compile_isolated
 from .support import (TestCase, captured_stdout, tag, temp_directory,
@@ -866,6 +866,43 @@ def impl(a, **kws):
         self.assertIn("use of VAR_KEYWORD (e.g. **kwargs) is unsupported", msg)
         self.assertIn("offending argument name is '**kws'", msg)
 
+    def test_overload_method_kwargs(self):
+        # Issue #3489
+        @overload_method(types.Array, 'foo')
+        def fooimpl(arr, a_kwarg=10):
+            def impl(arr, a_kwarg=10):
+                return a_kwarg
+            return impl
+
+        @njit
+        def bar(A):
+            return A.foo(), A.foo(20), A.foo(a_kwarg=30)
+
+        Z = np.arange(5)
+
+        self.assertEqual(bar(Z), (10, 20, 30))
+
+    def test_overload_method_literal_unpack(self):
+        # Issue #3683
+        @overload_method(types.Array, 'litfoo')
+        def litfoo(arr, val):
+            # Must be an integer
+            if isinstance(val, types.Integer):
+                # Must not be literal
+                if not isinstance(val, types.Literal):
+                    def impl(arr, val):
+                        return val
+                    return impl
+
+        @njit
+        def bar(A):
+            return A.litfoo(0xcafe)
+
+        A = np.zeros(1)
+        bar(A)
+        self.assertEqual(bar(A), 0xcafe)
+
+
 def _assert_cache_stats(cfunc, expect_hit, expect_misses):
     hit = cfunc._cache_hits[cfunc.signatures[0]]
     if hit != expect_hit: