# Notes on Literal Types

Note

This document describes an advanced feature designed to overcome some limitations of the compilation mechanism relating to types.

Some features need to specialize based on the literal value during compilation to produce type stable code necessary for successful compilation in Numba. This can be achieved by propagating the literal value through the type system. Numba recognizes inline literal values as `numba.types.Literal`. For example:

    def foo(x):
        a = 123
        return bar(x, a)

Numba will infer the type of `a` as `Literal[int](123)`. The definition of `bar()` can subsequently specialize its implementation knowing that the second argument is an `int` with the value `123`.

## `Literal` Type

Classes and methods related to the `Literal` type.

class numba.types.Literal(*args, **kwargs)

  Base class for Literal types. Literal types contain the original Python value in the type.

  A literal type should always be constructed from the literal(val) function.

numba.types.literal(value)

  Returns a Literal instance or raise LiteralTypingError

numba.types.unliteral(lit_type)

  Get base type from Literal type.

numba.types.maybe_literal(value)

  Get a Literal type for the value or None.

## Specifying for Literal Typing

To specify a value as a `Literal` type in code scheduled for JIT compilation, use the following function:

numba.literally(obj)

  Forces Numba to interpret obj as an Literal value.

  obj must be either a literal or an argument of the caller function, where the argument must be bound to a literal. The literal requirement propagates up the call stack.

  This function is intercepted by the compiler to alter the compilation behavior to wrap the corresponding function parameters as `Literal`. It has no effect outside of nopython-mode (interpreter, and objectmode).

  The current implementation detects literal arguments in two ways:

  1. Scans for uses of `literally` via a compiler pass.

  2. `literally` is overloaded to raise `numba.errors.ForceLiteralArg` to signal the dispatcher to treat the corresponding parameter differently. This mode is to support indirect use (via a function call).

  The execution semantic of this function is equivalent to an identity function.

  See numba/tests/test_literal_dispatch.py for examples.

### Code Example

from `test_literally_usage` of `numba/tests/doc_examples/test_literally_usage.py`

     1        import numba
     2
     3        def power(x, n):
     4            raise NotImplementedError
     5
     6        @numba.extending.overload(power)
     7        def ov_power(x, n):
     8            if isinstance(n, numba.types.Literal):
     9                # only if `n` is a literal
    10                if n.literal_value == 2:
    11                    # special case: square
    12                    print("square")
    13                    return lambda x, n: x * x
    14                elif n.literal_value == 3:
    15                    # special case: cubic
    16                    print("cubic")
    17                    return lambda x, n: x * x * x
    18            else:
    19                # If `n` is not literal, request literal dispatch
    20                return lambda x, n: numba.literally(n)
    21
    22            print("generic")
    23            return lambda x, n: x ** n
    24
    25        @numba.njit
    26        def test_power(x, n):
    27            return power(x, n)
    28
    29        # should print "square" and "9"
    30        print(test_power(3, 2))
    31
    32        # should print "cubic" and "27"
    33        print(test_power(3, 3))
    34
    35        # should print "generic" and "81"
    36        print(test_power(3, 4))
    37

### Internal Details

Internally, the compiler raises a `ForceLiteralArgs` exception to signal the dispatcher to wrap specified arguments using the `Literal` type.

class numba.errors.ForceLiteralArg(arg_indices, fold_arguments=None, loc=None)

  A Pseudo-exception to signal the dispatcher to type an argument literally

  Attributes:
    requested_args frozenset[int]

      requested positions of the arguments.

  __init__(arg_indices, fold_arguments=None, loc=None)
    Parameters:
      arg_indices Sequence[int]

        requested positions of the arguments.

      fold_arguments: callable

        A function `(tuple, dict) -> tuple` that binds and flattens the `args` and `kwargs`.

      loc numba.ir.Loc or None
  __or__(other)

    Same as self.combine(other)

  combine(other)

    Returns a new instance by or’ing the requested_args.

## Inside Extensions

`@overload` extensions can use `literally` inside the implementation body like in normal jit-code.

Explicit handling of literal requirements is possible through use of the following:

class numba.extending.SentryLiteralArgs(literal_args)
  Parameters:
    literal_args Sequence[str]

      A sequence of names for literal arguments

  Examples

  The following line:

      >>> SentryLiteralArgs(literal_args).for_pysig(pysig).bind(*args, **kwargs)

  is equivalent to:

      >>> sentry_literal_args(pysig, literal_args, args, kwargs)

  for_function(func)

    Bind the sentry to the signature of func.

    Parameters:
      func Function

        A python function.

    Returns:
      obj BoundLiteralArgs
  for_pysig(pysig)

    Bind the sentry to the given signature pysig.

    Parameters:
      pysig inspect.Signature
    Returns:
      obj BoundLiteralArgs

class numba.extending.BoundLiteralArgs(pysig, literal_args)

  This class is usually created by SentryLiteralArgs.

  bind(*args, **kwargs)

    Bind to argument types.

numba.extending.sentry_literal_args(pysig, literal_args, args, kwargs)

  Ensures that the given argument types (in args and kwargs) are literally typed for a function with the python signature pysig and the list of literal argument names in literal_args.

  Alternatively, this is the same as:

      SentryLiteralArgs(literal_args).for_pysig(pysig).bind(*args, **kwargs)