Example #1
0
 def object_dump(self, obj):
     """
     Dump a Python object on C stderr.  For debugging purposes.
     """
     fnty = Type.function(Type.void(), [self.pyobj])
     fn = self._get_function(fnty, name="_PyObject_Dump")
     return self.builder.call(fn, (obj,))
Example #2
0
def int_utruediv_impl(context, builder, sig, args):
    x, y = args
    fx = builder.uitofp(x, Type.double())
    fy = builder.uitofp(y, Type.double())
    cgutils.guard_zero(context, builder, y,
                       (ZeroDivisionError, "division by zero"))
    return builder.fdiv(fx, fy)
Example #3
0
def atan2_f32_impl(context, builder, sig, args):
    assert len(args) == 2
    mod = builder.module
    fnty = Type.function(Type.float(), [Type.float(), Type.float()])
    fn = cgutils.insert_pure_function(builder.module, fnty, name="atan2f")
    res = builder.call(fn, args)
    return impl_ret_untracked(context, builder, sig.return_type, res)
Example #4
0
def hypot_u64_impl(context, builder, sig, args):
    [x, y] = args
    y = builder.sitofp(y, Type.double())
    x = builder.sitofp(x, Type.double())
    fsig = signature(types.float64, types.float64, types.float64)
    res = hypot_float_impl(context, builder, fsig, (x, y))
    return impl_ret_untracked(context, builder, sig.return_type, res)
Example #5
0
    def string_as_string_and_size(self, strobj):
        """
        Returns a tuple of ``(ok, buffer, length)``.
        The ``ok`` is i1 value that is set if ok.
        The ``buffer`` is a i8* of the output buffer.
        The ``length`` is a i32/i64 (py_ssize_t) of the length of the buffer.
        """

        p_length = cgutils.alloca_once(self.builder, self.py_ssize_t)
        if PYVERSION >= (3, 0):
            fnty = Type.function(self.cstring, [self.pyobj,
                                                self.py_ssize_t.as_pointer()])
            fname = "PyUnicode_AsUTF8AndSize"
            fn = self._get_function(fnty, name=fname)

            buffer = self.builder.call(fn, [strobj, p_length])
            ok = self.builder.icmp_unsigned('!=',
                                            ir.Constant(buffer.type, None),
                                            buffer)
        else:
            fnty = Type.function(lc.Type.int(), [self.pyobj,
                                                 self.cstring.as_pointer(),
                                                 self.py_ssize_t.as_pointer()])
            fname = "PyString_AsStringAndSize"
            fn = self._get_function(fnty, name=fname)
            # Allocate space for the output parameters
            p_buffer = cgutils.alloca_once(self.builder, self.cstring)

            status = self.builder.call(fn, [strobj, p_buffer, p_length])

            negone = ir.Constant(status.type, -1)
            ok = self.builder.icmp_signed("!=", status, negone)
            buffer = self.builder.load(p_buffer)

        return (ok, buffer, self.builder.load(p_length))
Example #6
0
def wavebarrier_impl(context, builder, sig, args):
    assert not args
    fnty = Type.function(Type.void(), [])
    fn = builder.module.get_or_insert_function(fnty, name="__hsail_wavebarrier")
    fn.calling_convention = target.CC_SPIR_FUNC
    builder.call(fn, [])
    return _void_value
Example #7
0
 def call_function_pointer(self, builder, funcptr, signature, args, cconv=None):
     retty = self.get_value_type(signature.return_type)
     fnty = Type.function(retty, [a.type for a in args])
     fnptrty = Type.pointer(fnty)
     addr = self.get_constant(types.intp, funcptr)
     ptr = builder.inttoptr(addr, fnptrty)
     return builder.call(ptr, args, cconv=cconv)
Example #8
0
    def __init__(self, context, builder, value=None, ref=None, cast_ref=False):
        self._type = context.get_struct_type(self)
        self._context = context
        self._builder = builder
        if ref is None:
            self._value = alloca_once(builder, self._type)
            if value is not None:
                assert not is_pointer(value.type)
                assert value.type == self._type, (value.type, self._type)
                builder.store(value, self._value)
        else:
            assert value is None
            assert is_pointer(ref.type)
            if self._type != ref.type.pointee:
                if cast_ref:
                    ref = builder.bitcast(ref, Type.pointer(self._type))
                else:
                    raise TypeError(
                        "mismatching pointer type: got %s, expected %s"
                        % (ref.type.pointee, self._type))
            self._value = ref

        self._namemap = {}
        self._fdmap = []
        self._typemap = []
        base = Constant.int(Type.int(), 0)
        for i, (k, tp) in enumerate(self._fields):
            self._namemap[k] = i
            self._fdmap.append((base, Constant.int(Type.int(), i)))
            self._typemap.append(tp)
Example #9
0
 def restore_thread(self, thread_state):
     """
     Restore the given thread state by reacquiring the GIL.
     """
     fnty = Type.function(Type.void(), [self.voidptr])
     fn = self._get_function(fnty, name="PyEval_RestoreThread")
     self.builder.call(fn, [thread_state])
Example #10
0
def ptx_warp_sync(context, builder, sig, args):
    fname = 'llvm.nvvm.bar.warp.sync'
    lmod = builder.module
    fnty = Type.function(Type.void(), (Type.int(32),))
    sync = lmod.get_or_insert_function(fnty, name=fname)
    builder.call(sync, args)
    return context.get_dummy_value()
Example #11
0
 def numba_array_adaptor(self, ary, ptr):
     voidptr = Type.pointer(Type.int(8))
     fnty = Type.function(Type.int(), [self.pyobj, voidptr])
     fn = self._get_function(fnty, name="numba_adapt_ndarray")
     fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE)
     fn.args[1].add_attribute(lc.ATTR_NO_CAPTURE)
     return self.builder.call(fn, (ary, ptr))
Example #12
0
 def numba_buffer_adaptor(self, buf, ptr):
     fnty = Type.function(Type.void(),
                          [ir.PointerType(self.py_buffer_t), self.voidptr])
     fn = self._get_function(fnty, name="numba_adapt_buffer")
     fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE)
     fn.args[1].add_attribute(lc.ATTR_NO_CAPTURE)
     return self.builder.call(fn, (buf, ptr))
Example #13
0
def complex128_power_impl(context, builder, sig, args):
    [ca, cb] = args
    a = Complex128(context, builder, value=ca)
    b = Complex128(context, builder, value=cb)
    c = Complex128(context, builder)
    module = builder.module
    pa = a._getpointer()
    pb = b._getpointer()
    pc = c._getpointer()

    # Optimize for square because cpow looses a lot of precsiion
    TWO = context.get_constant(types.float64, 2)
    ZERO = context.get_constant(types.float64, 0)

    b_real_is_two = builder.fcmp(lc.FCMP_OEQ, b.real, TWO)
    b_imag_is_zero = builder.fcmp(lc.FCMP_OEQ, b.imag, ZERO)
    b_is_two = builder.and_(b_real_is_two, b_imag_is_zero)

    with builder.if_else(b_is_two) as (then, otherwise):
        with then:
            # Lower as multiplication
            res = complex_mul_impl(context, builder, sig, (ca, ca))
            cres = Complex128(context, builder, value=res)
            c.real = cres.real
            c.imag = cres.imag

        with otherwise:
            # Lower with call to external function
            fnty = Type.function(Type.void(), [pa.type] * 3)
            cpow = module.get_or_insert_function(fnty, name="numba.math.cpow")
            builder.call(cpow, (pa, pb, pc))

    res = builder.load(pc)
    return impl_ret_untracked(context, builder, sig.return_type, res)
Example #14
0
 def parse_tuple_and_keywords(self, args, kws, fmt, keywords, *objs):
     charptr = Type.pointer(Type.int(8))
     charptrary = Type.pointer(charptr)
     argtypes = [self.pyobj, self.pyobj, charptr, charptrary]
     fnty = Type.function(Type.int(), argtypes, var_arg=True)
     fn = self._get_function(fnty, name="PyArg_ParseTupleAndKeywords")
     return self.builder.call(fn, [args, kws, fmt, keywords] + list(objs))
Example #15
0
    def _long_from_native_int(self, ival, func_name, native_int_type,
                              signed):
        fnty = Type.function(self.pyobj, [native_int_type])
        fn = self._get_function(fnty, name=func_name)
        resptr = cgutils.alloca_once(self.builder, self.pyobj)

        if PYVERSION < (3, 0):
            # Under Python 2, we try to return a PyInt object whenever
            # the given number fits in a C long.
            pyint_fnty = Type.function(self.pyobj, [self.long])
            pyint_fn = self._get_function(pyint_fnty, name="PyInt_FromLong")
            long_max = Constant.int(native_int_type, _helperlib.long_max)
            if signed:
                long_min = Constant.int(native_int_type, _helperlib.long_min)
                use_pyint = self.builder.and_(
                    self.builder.icmp(lc.ICMP_SGE, ival, long_min),
                    self.builder.icmp(lc.ICMP_SLE, ival, long_max),
                    )
            else:
                use_pyint = self.builder.icmp(lc.ICMP_ULE, ival, long_max)

            with self.builder.if_else(use_pyint) as (then, otherwise):
                with then:
                    downcast_ival = self.builder.trunc(ival, self.long)
                    res = self.builder.call(pyint_fn, [downcast_ival])
                    self.builder.store(res, resptr)
                with otherwise:
                    res = self.builder.call(fn, [ival])
                    self.builder.store(res, resptr)
        else:
            fn = self._get_function(fnty, name=func_name)
            self.builder.store(self.builder.call(fn, [ival]), resptr)

        return self.builder.load(resptr)
Example #16
0
def set_branch_weight(builder, brinst, trueweight, falseweight):
    module = get_module(builder)
    mdid = lc.MetaDataString.get(module, "branch_weights")
    trueweight = lc.Constant.int(Type.int(), trueweight)
    falseweight = lc.Constant.int(Type.int(), falseweight)
    md = lc.MetaData.get(module, [mdid, trueweight, falseweight])
    brinst.set_metadata("prof", md)
Example #17
0
    def insert_string_const_addrspace(self, builder, string):
        """
        Insert a constant string in the constant addresspace and return a
        generic i8 pointer to the data.

        This function attempts to deduplicate.
        """
        lmod = builder.basic_block.function.module
        text = Constant.stringz(string)
        name = "__conststring__.%s" % string
        charty = Type.int(8)

        for gv in lmod.global_variables:
            if gv.name == name and gv.type.pointee == text.type:
                break
        else:
            gv = lmod.add_global_variable(text.type, name=name,
                                          addrspace=nvvm.ADDRSPACE_CONSTANT)
            gv.linkage = LINKAGE_INTERNAL
            gv.global_constant = True
            gv.initializer = text

        constcharptrty = Type.pointer(charty, nvvm.ADDRSPACE_CONSTANT)
        charptr = builder.bitcast(gv, constcharptrty)

        conv = nvvmutils.insert_addrspace_conv(lmod, charty,
                                               nvvm.ADDRSPACE_CONSTANT)
        return builder.call(conv, [charptr])
Example #18
0
 def nrt_adapt_ndarray_from_python(self, ary, ptr):
     assert self.context.enable_nrt
     fnty = Type.function(Type.int(), [self.pyobj, self.voidptr])
     fn = self._get_function(fnty, name="NRT_adapt_ndarray_from_python")
     fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE)
     fn.args[1].add_attribute(lc.ATTR_NO_CAPTURE)
     return self.builder.call(fn, (ary, ptr))
Example #19
0
 def tuple_setitem(self, tuple_val, index, item):
     """
     Steals a reference to `item`.
     """
     fnty = Type.function(Type.int(), [self.pyobj, Type.int(), self.pyobj])
     setitem_fn = self._get_function(fnty, name='PyTuple_SetItem')
     index = self.context.get_constant(types.int32, index)
     self.builder.call(setitem_fn, [tuple_val, index, item])
Example #20
0
def ptx_threadfence_device(context, builder, sig, args):
    assert not args
    fname = "llvm.nvvm.membar.gl"
    lmod = builder.module
    fnty = Type.function(Type.void(), ())
    sync = lmod.get_or_insert_function(fnty, name=fname)
    builder.call(sync, ())
    return context.get_dummy_value()
Example #21
0
 def err_set_string(self, exctype, msg):
     fnty = Type.function(Type.void(), [self.pyobj, self.cstring])
     fn = self._get_function(fnty, name="PyErr_SetString")
     if isinstance(exctype, str):
         exctype = self.get_c_object(exctype)
     if isinstance(msg, str):
         msg = self.context.insert_const_string(self.module, msg)
     return self.builder.call(fn, (exctype, msg))
Example #22
0
 def list_setitem(self, seq, idx, val):
     """
     Warning: Steals reference to ``val``
     """
     fnty = Type.function(Type.int(), [self.pyobj, self.py_ssize_t,
                                       self.pyobj])
     fn = self._get_function(fnty, name="PyList_SetItem")
     return self.builder.call(fn, [seq, idx, val])
Example #23
0
 def core(context, builder, sig, args):
     assert sig.return_type == types.boolean, nvname
     fty = context.get_value_type(ty)
     lmod = builder.module
     fnty = Type.function(Type.int(), [fty])
     fn = lmod.get_or_insert_function(fnty, name=nvname)
     result = builder.call(fn, args)
     return context.cast(builder, result, types.int32, types.boolean)
Example #24
0
 def nrt_adapt_buffer_from_python(self, buf, ptr):
     assert self.context.enable_nrt
     fnty = Type.function(Type.void(), [Type.pointer(self.py_buffer_t),
                                        self.voidptr])
     fn = self._get_function(fnty, name="NRT_adapt_buffer_from_python")
     fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE)
     fn.args[1].add_attribute(lc.ATTR_NO_CAPTURE)
     return self.builder.call(fn, (buf, ptr))
Example #25
0
def ptx_syncthreads(context, builder, sig, args):
    assert not args
    fname = 'llvm.nvvm.barrier0'
    lmod = cgutils.get_module(builder)
    fnty = Type.function(Type.void(), ())
    sync = lmod.get_or_insert_function(fnty, name=fname)
    builder.call(sync, ())
    return context.get_dummy_value()
Example #26
0
def atan2_f64_impl(context, builder, sig, args):
    assert len(args) == 2
    mod = cgutils.get_module(builder)
    fnty = Type.function(Type.double(), [Type.double(), Type.double()])
    # Workaround atan2() issues under Windows
    fname = "atan2_fixed" if sys.platform == "win32" else "atan2"
    fn = mod.get_or_insert_function(fnty, name=fname)
    return builder.call(fn, args)
Example #27
0
def is_scalar_neg(builder, value):
    """is _value_ negative?. Assumes _value_ is signed"""
    nullval = Constant.null(value.type)
    if value.type in (Type.float(), Type.double()):
        isneg = builder.fcmp(lc.FCMP_OLT, value, nullval)
    else:
        isneg = builder.icmp(lc.ICMP_SLT, value, nullval)
    return isneg
Example #28
0
 def implementer(context, builder, sig, args):
     [val] = args
     input_type = sig.args[0]
     if input_type.signed:
         fpval = builder.sitofp(val, Type.double())
     else:
         fpval = builder.uitofp(val, Type.double())
     sig = signature(types.float64, types.float64)
     return wrapped_impl(context, builder, sig, [fpval])
Example #29
0
File: base.py Project: yuguen/numba
 def printf(self, builder, format_string, *args):
     mod = builder.module
     if isinstance(format_string, str):
         cstr = self.insert_const_string(mod, format_string)
     else:
         cstr = format_string
     fnty = Type.function(Type.int(), (GENERIC_POINTER,), var_arg=True)
     fn = mod.get_or_insert_function(fnty, "printf")
     return builder.call(fn, (cstr,) + tuple(args))
Example #30
0
def atan2_f64_impl(context, builder, sig, args):
    assert len(args) == 2
    mod = builder.module
    fnty = Type.function(Type.double(), [Type.double(), Type.double()])
    # Workaround atan2() issues under Windows
    fname = "atan2_fixed" if sys.platform == "win32" else "atan2"
    fn = cgutils.insert_pure_function(builder.module, fnty, name=fname)
    res = builder.call(fn, args)
    return impl_ret_untracked(context, builder, sig.return_type, res)
Example #31
0
 def set_add(self, set, value):
     fnty = Type.function(Type.int(), [self.pyobj, self.pyobj])
     fn = self._get_function(fnty, name="PySet_Add")
     return self.builder.call(fn, [set, value])
Example #32
0
 def number_invert(self, obj):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PyNumber_Invert")
     return self.builder.call(fn, (obj, ))
Example #33
0
 def print_string(self, builder, text):
     mod = builder.module
     cstring = GENERIC_POINTER
     fnty = Type.function(Type.int(), [cstring])
     puts = cgutils.get_or_insert_function(mod, fnty, "puts")
     return builder.call(puts, [text])
Example #34
0
 def complex_imag_as_double(self, cobj):
     fnty = Type.function(Type.double(), [self.pyobj])
     fn = self._get_function(fnty, name="PyComplex_ImagAsDouble")
     return self.builder.call(fn, [cobj])
Example #35
0
def get_record_member(builder, record, offset, typ):
    pval = gep_inbounds(builder, record, 0, offset)
    assert not is_pointer(pval.type.pointee)
    return builder.bitcast(pval, Type.pointer(typ))
Example #36
0
 def tuple_size(self, tup):
     fnty = Type.function(self.py_ssize_t, [self.pyobj])
     fn = self._get_function(fnty, name="PyTuple_Size")
     return self.builder.call(fn, [tup])
Example #37
0
 def sequence_tuple(self, obj):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PySequence_Tuple")
     return self.builder.call(fn, [obj])
Example #38
0
 def object_getitem(self, obj, key):
     fnty = Type.function(self.pyobj, [self.pyobj, self.pyobj])
     fn = self._get_function(fnty, name="PyObject_GetItem")
     return self.builder.call(fn, (obj, key))
Example #39
0
 def object_setattr(self, obj, attr, val):
     fnty = Type.function(Type.int(), [self.pyobj, self.pyobj, self.pyobj])
     fn = self._get_function(fnty, name="PyObject_SetAttr")
     return self.builder.call(fn, [obj, attr, val])
Example #40
0
 def object_str(self, obj):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PyObject_Str")
     return self.builder.call(fn, [obj])
Example #41
0
 def object_setitem(self, obj, key, val):
     fnty = Type.function(Type.int(), [self.pyobj, self.pyobj, self.pyobj])
     fn = self._get_function(fnty, name="PyObject_SetItem")
     return self.builder.call(fn, (obj, key, val))
Example #42
0
 def sys_write_stdout(self, fmt, *args):
     fnty = Type.function(Type.void(), [self.cstring], var_arg=True)
     fn = self._get_function(fnty, name="PySys_WriteStdout")
     return self.builder.call(fn, (fmt, ) + args)
Example #43
0
"""

from __future__ import print_function, division, absolute_import

import collections
from contextlib import contextmanager
import functools
import re

from llvmlite import ir
from llvmlite.llvmpy.core import Constant, Type
import llvmlite.llvmpy.core as lc

from . import utils

true_bit = Constant.int(Type.int(1), 1)
false_bit = Constant.int(Type.int(1), 0)
true_byte = Constant.int(Type.int(8), 1)
false_byte = Constant.int(Type.int(8), 0)

intp_t = Type.int(utils.MACHINE_BITS)


def as_bool_byte(builder, value):
    return builder.zext(value, Type.int(8))


def as_bool_bit(builder, value):
    return builder.icmp(lc.ICMP_NE, value, Constant.null(value.type))

Example #44
0
def as_bool_byte(builder, value):
    return builder.zext(value, Type.int(8))
Example #45
0
 def set_new(self, iterable=None):
     if iterable is None:
         iterable = self.get_null_object()
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PySet_New")
     return self.builder.call(fn, [iterable])
Example #46
0
 def object_setattr_string(self, obj, attr, val):
     cstr = self.context.insert_const_string(self.module, attr)
     fnty = Type.function(Type.int(),
                          [self.pyobj, self.cstring, self.pyobj])
     fn = self._get_function(fnty, name="PyObject_SetAttrString")
     return self.builder.call(fn, [obj, cstr, val])
Example #47
0
 def tuple_new(self, count):
     fnty = Type.function(self.pyobj, [Type.int()])
     fn = self._get_function(fnty, name='PyTuple_New')
     return self.builder.call(
         fn, [self.context.get_constant(types.int32, count)])
Example #48
0
 def object_getattr(self, obj, attr):
     fnty = Type.function(self.pyobj, [self.pyobj, self.pyobj])
     fn = self._get_function(fnty, name="PyObject_GetAttr")
     return self.builder.call(fn, [obj, attr])
Example #49
0
 def list_new(self, szval):
     fnty = Type.function(self.pyobj, [self.py_ssize_t])
     fn = self._get_function(fnty, name="PyList_New")
     return self.builder.call(fn, [szval])
Example #50
0
 def complex_from_doubles(self, realval, imagval):
     fnty = Type.function(self.pyobj, [Type.double(), Type.double()])
     fn = self._get_function(fnty, name="PyComplex_FromDoubles")
     return self.builder.call(fn, [realval, imagval])
Example #51
0
 def sequence_getslice(self, obj, start, stop):
     fnty = Type.function(self.pyobj,
                          [self.pyobj, self.py_ssize_t, self.py_ssize_t])
     fn = self._get_function(fnty, name="PySequence_GetSlice")
     return self.builder.call(fn, (obj, start, stop))
Example #52
0
 def bool_from_long(self, ival):
     fnty = Type.function(self.pyobj, [self.long])
     fn = self._get_function(fnty, name="PyBool_FromLong")
     return self.builder.call(fn, [ival])
Example #53
0
 def iter_next(self, iterobj):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PyIter_Next")
     return self.builder.call(fn, [iterobj])
Example #54
0
 def number_float(self, val):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PyNumber_Float")
     return self.builder.call(fn, [val])
Example #55
0
 def object_not(self, obj):
     fnty = Type.function(Type.int(), [self.pyobj])
     fn = self._get_function(fnty, name="PyObject_Not")
     return self.builder.call(fn, [obj])
Example #56
0
 def call_function_objargs(self, callee, objargs):
     fnty = Type.function(self.pyobj, [self.pyobj], var_arg=True)
     fn = self._get_function(fnty, name="PyObject_CallFunctionObjArgs")
     args = [callee] + list(objargs)
     args.append(self.context.get_constant_null(types.pyobject))
     return self.builder.call(fn, args)
Example #57
0
 def float_as_double(self, fobj):
     fnty = Type.function(self.double, [self.pyobj])
     fn = self._get_function(fnty, name="PyFloat_AsDouble")
     return self.builder.call(fn, [fobj])
Example #58
0
 def import_module_noblock(self, modname):
     fnty = Type.function(self.pyobj, [self.cstring])
     fn = self._get_function(fnty, name="PyImport_ImportModuleNoBlock")
     return self.builder.call(fn, [modname])
Example #59
0
 def number_positive(self, obj):
     fnty = Type.function(self.pyobj, [self.pyobj])
     fn = self._get_function(fnty, name="PyNumber_Positive")
     return self.builder.call(fn, (obj, ))
Example #60
0
from llvmlite import ir as llvmir
import llvmlite.llvmpy.core as lc
from llvmlite.llvmpy.core import Type, Constant, LLVMException
import llvmlite.binding as ll

from numba.core import types, utils, typing, datamodel, debuginfo, funcdesc, config, cgutils, imputils
from numba.core import event, errors, targetconfig
from numba import _dynfunc, _helperlib
from numba.core.compiler_lock import global_compiler_lock
from numba.core.pythonapi import PythonAPI
from numba.core.imputils import (user_function, user_generator,
                       builtin_registry, impl_ret_borrowed,
                       RegistryLoader)
from numba.cpython import builtins

GENERIC_POINTER = Type.pointer(Type.int(8))
PYOBJECT = GENERIC_POINTER
void_ptr = GENERIC_POINTER


class OverloadSelector(object):
    """
    An object matching an actual signature against a registry of formal
    signatures and choosing the best candidate, if any.

    In the current implementation:
    - a "signature" is a tuple of type classes or type instances
    - the "best candidate" is the most specific match
    """

    def __init__(self):