Repository: numba/numba

Issue:
literally doesn't work in overload_method
<!--

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:

-->

- [ ] 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!

-->

```python
import numba
from numba.extending import (overload_method, overload, models, register_model, intrinsic)
from numba.special import literally
from numba.typing import signature
from numba import cgutils

class Dummy:
    def lit(self, a):
        return a

class DummyType(numba.types.Type):
    def __init__(self):
        super().__init__(name="dummy")

@register_model(DummyType)
class DummyTypeModel(models.StructModel):
    def __init__(self, dmm, fe_type):
        members = []
        super().__init__(dmm, fe_type, members)

@intrinsic
def init_dummy(typingctx):
    def codegen(context, builder, signature, args):
        dummy = cgutils.create_struct_proxy(
            signature.return_type)(context, builder)

        return dummy._getvalue()

    sig = signature(DummyType())
    return sig, codegen

@overload(Dummy)
def dummy_overload():
    def ctor():
        return init_dummy()

    return ctor

@overload_method(DummyType, 'lit')
def lit_overload(self, a):
    def impl(self, a):
        return literally(a)
        # return a # <- work fine if do simple return

    return impl

def test_impl(a):
    d = Dummy()

    return d.lit(a)

jtest = numba.njit(test_impl)
print(jtest(5))
```
Output:
```
======================================================================
ERROR: test_literally_with_overload_method (tests.test_hpat_jit.TestHpatJitIssues)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/data/hpat_fork/hpat_2/sdc/tests/test_hpat_jit.py", line 492, in test_literally_with_overload_method
    print(jtest(5))
  File "/mnt/data/numba_git/numba_master_master/numba/dispatcher.py", line 401, in _compile_for_args
    error_rewrite(e, 'typing')
  File "/mnt/data/numba_git/numba_master_master/numba/dispatcher.py", line 344, in error_rewrite
    reraise(type(e), e, None)
  File "/mnt/data/numba_git/numba_master_master/numba/six.py", line 668, in reraise
    raise value.with_traceback(tb)
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Internal error at <numba.typeinfer.CallConstraint object at 0x7f1724938da0>.
missing a required argument: 'a'
[1] During: resolving callee type: BoundFunction((<class 'tests.test_hpat_jit.TestHpatJitIssues.test_literally_with_overload_method.<locals>.DummyType'>, 'lit') for dummy)
[2] During: typing of call at /mnt/data/hpat_fork/hpat_2/sdc/tests/test_hpat_jit.py (488)

Enable logging at debug level for details.

File "tests/test_hpat_jit.py", line 488:
        def test_impl(a):
            <source elided>

            return d.lit(a)
            ^
```

Please note, works fine with simple `overload`:
```python
import numba
from numba.extending import overload
from numba.special import literally


def lit(a):
    return a

@overload(lit)
def lit_overload(a):
    def impl(a):
        return literally(a)

    return impl

def test_impl(a):
    return lit(a)

jtest = numba.njit(test_impl)
print(jtest(5))
```
Output:
```
5
```

numba version: 6d7cddb9084d843e12bcdf9c443bc9cce56aa02f (Start cycle for 0.48.0)
literally doesn't work in overload_method
<!--

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:

-->

- [ ] 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!

-->

```python
import numba
from numba.extending import (overload_method, overload, models, register_model, intrinsic)
from numba.special import literally
from numba.typing import signature
from numba import cgutils

class Dummy:
    def lit(self, a):
        return a

class DummyType(numba.types.Type):
    def __init__(self):
        super().__init__(name="dummy")

@register_model(DummyType)
class DummyTypeModel(models.StructModel):
    def __init__(self, dmm, fe_type):
        members = []
        super().__init__(dmm, fe_type, members)

@intrinsic
def init_dummy(typingctx):
    def codegen(context, builder, signature, args):
        dummy = cgutils.create_struct_proxy(
            signature.return_type)(context, builder)

        return dummy._getvalue()

    sig = signature(DummyType())
    return sig, codegen

@overload(Dummy)
def dummy_overload():
    def ctor():
        return init_dummy()

    return ctor

@overload_method(DummyType, 'lit')
def lit_overload(self, a):
    def impl(self, a):
        return literally(a)
        # return a # <- work fine if do simple return

    return impl

def test_impl(a):
    d = Dummy()

    return d.lit(a)

jtest = numba.njit(test_impl)
print(jtest(5))
```
Output:
```
======================================================================
ERROR: test_literally_with_overload_method (tests.test_hpat_jit.TestHpatJitIssues)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/data/hpat_fork/hpat_2/sdc/tests/test_hpat_jit.py", line 492, in test_literally_with_overload_method
    print(jtest(5))
  File "/mnt/data/numba_git/numba_master_master/numba/dispatcher.py", line 401, in _compile_for_args
    error_rewrite(e, 'typing')
  File "/mnt/data/numba_git/numba_master_master/numba/dispatcher.py", line 344, in error_rewrite
    reraise(type(e), e, None)
  File "/mnt/data/numba_git/numba_master_master/numba/six.py", line 668, in reraise
    raise value.with_traceback(tb)
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Internal error at <numba.typeinfer.CallConstraint object at 0x7f1724938da0>.
missing a required argument: 'a'
[1] During: resolving callee type: BoundFunction((<class 'tests.test_hpat_jit.TestHpatJitIssues.test_literally_with_overload_method.<locals>.DummyType'>, 'lit') for dummy)
[2] During: typing of call at /mnt/data/hpat_fork/hpat_2/sdc/tests/test_hpat_jit.py (488)

Enable logging at debug level for details.

File "tests/test_hpat_jit.py", line 488:
        def test_impl(a):
            <source elided>

            return d.lit(a)
            ^
```

Please note, works fine with simple `overload`:
```python
import numba
from numba.extending import overload
from numba.special import literally


def lit(a):
    return a

@overload(lit)
def lit_overload(a):
    def impl(a):
        return literally(a)

    return impl

def test_impl(a):
    return lit(a)

jtest = numba.njit(test_impl)
print(jtest(5))
```
Output:
```
5
```

numba version: 6d7cddb9084d843e12bcdf9c443bc9cce56aa02f (Start cycle for 0.48.0)


Discussion:
Thanks for the report, I can reproduce. After a quick look it seems like there's some bad interaction with the pseudo-exception generation used to make `literally` work and the constraint for the `@overload_method`.
Thanks for the report, I can reproduce. After a quick look it seems like there's some bad interaction with the pseudo-exception generation used to make `literally` work and the constraint for the `@overload_method`.

Resolving patch:
diff --git a/numba/typeinfer.py b/numba/typeinfer.py
--- a/numba/typeinfer.py
+++ b/numba/typeinfer.py
@@ -505,7 +505,11 @@ def resolve(self, typeinfer, typevars, fnty):
         try:
             sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
         except ForceLiteralArg as e:
-            folded = e.fold_arguments(self.args, self.kws)
+            # Adjust for bound methods
+            folding_args = ((fnty.this,) + tuple(self.args)
+                            if isinstance(fnty, types.BoundFunction)
+                            else self.args)
+            folded = e.fold_arguments(folding_args, self.kws)
             requested = set()
             unsatisified = set()
             for idx in e.requested_args:


Test patch:
diff --git a/numba/tests/test_literal_dispatch.py b/numba/tests/test_literal_dispatch.py
--- a/numba/tests/test_literal_dispatch.py
+++ b/numba/tests/test_literal_dispatch.py
@@ -5,8 +5,12 @@
 import numba
 import numba.unittest_support as unittest
 from numba.tests.support import TestCase
-from numba import njit, types, errors
-from numba.extending import overload, SentryLiteralArgs
+from numba import njit, types, errors, cgutils
+from numba.typing import signature
+from numba.datamodel import models
+from numba.extending import (
+    overload, SentryLiteralArgs, overload_method, register_model, intrinsic,
+)
 from numba.special import literally
 
 
@@ -326,5 +330,72 @@ def foo(a, b):
                       str(raises.exception))
 
 
+class TestLiteralDispatchWithCustomType(TestCase):
+    def make_dummy_type(self):
+        class Dummy(object):
+            def lit(self, a):
+                return a
+
+        class DummyType(numba.types.Type):
+            def __init__(self):
+                super(DummyType, self).__init__(name="dummy")
+
+        @register_model(DummyType)
+        class DummyTypeModel(models.StructModel):
+            def __init__(self, dmm, fe_type):
+                members = []
+                super(DummyTypeModel, self).__init__(dmm, fe_type, members)
+
+        @intrinsic
+        def init_dummy(typingctx):
+            def codegen(context, builder, signature, args):
+                dummy = cgutils.create_struct_proxy(
+                    signature.return_type)(context, builder)
+
+                return dummy._getvalue()
+
+            sig = signature(DummyType())
+            return sig, codegen
+
+        @overload(Dummy)
+        def dummy_overload():
+            def ctor():
+                return init_dummy()
+
+            return ctor
+
+        return (DummyType, Dummy)
+
+    def test_overload_method(self):
+        # from issue #5011
+        DummyType, Dummy = self.make_dummy_type()
+
+        @overload_method(DummyType, 'lit')
+        def lit_overload(self, a):
+            def impl(self, a):
+                return literally(a)  # <-- using literally here
+
+            return impl
+
+        @njit
+        def test_impl(a):
+            d = Dummy()
+
+            return d.lit(a)
+
+        # Successful case
+        self.assertEqual(test_impl(5), 5)
+
+        # Failing case
+        @njit
+        def inside(a):
+            return test_impl(a + 1)
+
+        with self.assertRaises(errors.TypingError) as raises:
+            inside(4)
+
+        self.assertIn("Cannot request literal type.", str(raises.exception))
+
+
 if __name__ == '__main__':
     unittest.main()