Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid calling typers/overloads without literal types #5411

Open
ehsantn opened this issue Mar 23, 2020 · 3 comments
Open

Avoid calling typers/overloads without literal types #5411

ehsantn opened this issue Mar 23, 2020 · 3 comments

Comments

@ehsantn
Copy link
Collaborator

ehsantn commented Mar 23, 2020

Feature request

Type inference currently calls every typing template twice: once using literal types if available, and again without literal types. I believe the original purpose was backwards compatibility when literal types were introduced.

However, this can be confusing for developers. For example, if an overload expects a literal type but fails due to another reason, Numba tries again without literal types. The developer doesn't see the original error, and may be confused why the overload is called without literal types (our engineers have experienced these issues many times).

Ideally, all templates should handle literal types so Numba wouldn't need to have this behavior. In the short term, there could be an option for overloads (e.g. "literal_only=True").

@sklam
Copy link
Member

sklam commented Mar 31, 2020

A literal_only option would mean that every args must be of literal type. But, that's often not the case. Instead, there is an alternative that let you mark which argument must be of literal type. See numba.core.extending.SentryLiteralArgs. Example

@overload(do_this)
def ov_do_this(x, y):
SentryLiteralArgs(['x']).for_function(ov_do_this).bind(x, y)
return lambda x, y: x + y

@ehsantn
Copy link
Collaborator Author

ehsantn commented Mar 31, 2020

Thanks @sklam . But my point is about avoiding rerun of overload function with unliteraled types if first overload implementation failed to compile. This solution is still confusing if there was an error in overload:

import numba
from numba.extending import overload, SentryLiteralArgs

def do_this(x, y):
    return x + y

@overload(do_this)
def ov_do_this(x, y):
    SentryLiteralArgs(['x']).for_function(ov_do_this).bind(x, y)
    return lambda x, y: x + y + UNKNOWN_GLOBAL  # invalid impl

@numba.njit
def foo(a, b):
    return do_this(a, b)

foo(1, 3)
numba.errors.ForceLiteralArg: Failed in nopython mode pipeline (step: nopython frontend)
Pseudo-exception to force literal arguments in the dispatcher

File "../tmp/sentry_test.py", line 14:
def foo(a, b):
    return do_this(a, b)
    ^

[1] During: resolving callee type: Function(<function do_this at 0x10286a170>)
[2] During: typing of call at ../tmp/sentry_test.py (14)


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/ehsan/dev/numba/numba/dispatcher.py", line 353, in _compile_for_args
    return self.compile(tuple(argtypes))
  File "/Users/ehsan/dev/numba/numba/compiler_lock.py", line 32, in _acquire_compile_lock
    return func(*args, **kwargs)
  File "/Users/ehsan/dev/numba/numba/dispatcher.py", line 772, in compile
    raise e.bind_fold_arguments(folded)
numba.errors.ForceLiteralArg: Pseudo-exception to force literal arguments in the dispatcher

File "../tmp/sentry_test.py", line 14:
def foo(a, b):
    return do_this(a, b)
    ^


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "../tmp/sentry_test.py", line 17, in <module>
    foo(1, 3)
  File "/Users/ehsan/dev/numba/numba/dispatcher.py", line 376, in _compile_for_args
    return self._compile_for_args(*args)
  File "/Users/ehsan/dev/numba/numba/dispatcher.py", line 369, in _compile_for_args
    raise errors.CompilerError(m.format(info))
numba.errors.CompilerError: Repeated literal typing request.
Arg #0 is Literal[int](1).
This is likely caused by an error in typing. Please see nested and suppressed exceptions.

@ARF1
Copy link
Contributor

ARF1 commented Mar 31, 2020

I also have an issue that related to this that really confused me in the beginning.

Say I have code like this:

@overload(do_this) 
def ov_do_this(x, y):
    if not isinstance(x, types.Integer):
        raise TypeError('do_this: parameter x may only be an integer')
    if not isinstance(y, types.Literal):
        raise RequireLiteralValue('do_this: parameter y must be literal')
   pass

Let's say this is called with do_this(2.0, 1) this results in the error message LiteralValueRequired: do_this: parameter y must be literal - despite it being a literal!

When I first encountered this, I was searching high and low in my code why numba did not recognize 1 as a literal! - It took me about a whole day of randomly tinkering with my code until I realized that after the first TypeError numba calls the overload again with y as a non-literal which then results in the confusing error message.

Not sure if and how the user experience could be improved though.

Once one know one has to scroll up to identify the real issue it is not a big problem but it is far from obvious to somebody new to numba.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants