1""" 2Define typing templates 3""" 4 5from abc import ABC, abstractmethod 6import functools 7import sys 8import inspect 9import os.path 10from collections import namedtuple 11from collections.abc import Sequence 12from types import MethodType, FunctionType 13 14import numba 15from numba.core import types, utils 16from numba.core.errors import TypingError, InternalError 17from numba.core.cpu_options import InlineOptions 18 19# info store for inliner callback functions e.g. cost model 20_inline_info = namedtuple('inline_info', 21 'func_ir typemap calltypes signature') 22 23 24class Signature(object): 25 """ 26 The signature of a function call or operation, i.e. its argument types 27 and return type. 28 """ 29 30 # XXX Perhaps the signature should be a BoundArguments, instead 31 # of separate args and pysig... 32 __slots__ = '_return_type', '_args', '_recvr', '_pysig' 33 34 def __init__(self, return_type, args, recvr, pysig=None): 35 if isinstance(args, list): 36 args = tuple(args) 37 self._return_type = return_type 38 self._args = args 39 self._recvr = recvr 40 self._pysig = pysig 41 42 @property 43 def return_type(self): 44 return self._return_type 45 46 @property 47 def args(self): 48 return self._args 49 50 @property 51 def recvr(self): 52 return self._recvr 53 54 @property 55 def pysig(self): 56 return self._pysig 57 58 def replace(self, **kwargs): 59 """Copy and replace the given attributes provided as keyword arguments. 60 Returns an updated copy. 61 """ 62 curstate = dict(return_type=self.return_type, 63 args=self.args, 64 recvr=self.recvr, 65 pysig=self.pysig) 66 curstate.update(kwargs) 67 return Signature(**curstate) 68 69 def __getstate__(self): 70 """ 71 Needed because of __slots__. 72 """ 73 return self._return_type, self._args, self._recvr, self._pysig 74 75 def __setstate__(self, state): 76 """ 77 Needed because of __slots__. 78 """ 79 self._return_type, self._args, self._recvr, self._pysig = state 80 81 def __hash__(self): 82 return hash((self.args, self.return_type)) 83 84 def __eq__(self, other): 85 if isinstance(other, Signature): 86 return (self.args == other.args and 87 self.return_type == other.return_type and 88 self.recvr == other.recvr and 89 self.pysig == other.pysig) 90 91 def __ne__(self, other): 92 return not (self == other) 93 94 def __repr__(self): 95 return "%s -> %s" % (self.args, self.return_type) 96 97 @property 98 def is_method(self): 99 """ 100 Whether this signature represents a bound method or a regular 101 function. 102 """ 103 return self.recvr is not None 104 105 def as_method(self): 106 """ 107 Convert this signature to a bound method signature. 108 """ 109 if self.recvr is not None: 110 return self 111 sig = signature(self.return_type, *self.args[1:], 112 recvr=self.args[0]) 113 114 # Adjust the python signature 115 params = list(self.pysig.parameters.values())[1:] 116 sig = sig.replace( 117 pysig=utils.pySignature( 118 parameters=params, 119 return_annotation=self.pysig.return_annotation, 120 ), 121 ) 122 return sig 123 124 def as_function(self): 125 """ 126 Convert this signature to a regular function signature. 127 """ 128 if self.recvr is None: 129 return self 130 sig = signature(self.return_type, *((self.recvr,) + self.args)) 131 return sig 132 133 def as_type(self): 134 """ 135 Convert this signature to a first-class function type. 136 """ 137 return types.FunctionType(self) 138 139 def __unliteral__(self): 140 return signature(types.unliteral(self.return_type), 141 *map(types.unliteral, self.args)) 142 143 def dump(self, tab=''): 144 c = self.as_type()._code 145 print(f'{tab}DUMP {type(self).__name__} [type code: {c}]') 146 print(f'{tab} Argument types:') 147 for a in self.args: 148 a.dump(tab=tab + ' | ') 149 print(f'{tab} Return type:') 150 self.return_type.dump(tab=tab + ' | ') 151 print(f'{tab}END DUMP') 152 153 def is_precise(self): 154 for atype in self.args: 155 if not atype.is_precise(): 156 return False 157 return self.return_type.is_precise() 158 159 160def make_concrete_template(name, key, signatures): 161 baseclasses = (ConcreteTemplate,) 162 gvars = dict(key=key, cases=list(signatures)) 163 return type(name, baseclasses, gvars) 164 165 166def make_callable_template(key, typer, recvr=None): 167 """ 168 Create a callable template with the given key and typer function. 169 """ 170 def generic(self): 171 return typer 172 173 name = "%s_CallableTemplate" % (key,) 174 bases = (CallableTemplate,) 175 class_dict = dict(key=key, generic=generic, recvr=recvr) 176 return type(name, bases, class_dict) 177 178 179def signature(return_type, *args, **kws): 180 recvr = kws.pop('recvr', None) 181 assert not kws 182 return Signature(return_type, args, recvr=recvr) 183 184 185def fold_arguments(pysig, args, kws, normal_handler, default_handler, 186 stararg_handler): 187 """ 188 Given the signature *pysig*, explicit *args* and *kws*, resolve 189 omitted arguments and keyword arguments. A tuple of positional 190 arguments is returned. 191 Various handlers allow to process arguments: 192 - normal_handler(index, param, value) is called for normal arguments 193 - default_handler(index, param, default) is called for omitted arguments 194 - stararg_handler(index, param, values) is called for a "*args" argument 195 """ 196 if isinstance(kws, Sequence): 197 # Normalize dict kws 198 kws = dict(kws) 199 200 # deal with kwonly args 201 params = pysig.parameters 202 kwonly = [] 203 for name, p in params.items(): 204 if p.kind == p.KEYWORD_ONLY: 205 kwonly.append(name) 206 207 if kwonly: 208 bind_args = args[:-len(kwonly)] 209 else: 210 bind_args = args 211 bind_kws = kws.copy() 212 if kwonly: 213 for idx, n in enumerate(kwonly): 214 bind_kws[n] = args[len(kwonly) + idx] 215 216 # now bind 217 ba = pysig.bind(*bind_args, **bind_kws) 218 for i, param in enumerate(pysig.parameters.values()): 219 name = param.name 220 default = param.default 221 if param.kind == param.VAR_POSITIONAL: 222 # stararg may be omitted, in which case its "default" value 223 # is simply the empty tuple 224 if name in ba.arguments: 225 argval = ba.arguments[name] 226 # NOTE: avoid wrapping the tuple type for stararg in another 227 # tuple. 228 if (len(argval) == 1 and 229 isinstance(argval[0], (types.StarArgTuple, 230 types.StarArgUniTuple))): 231 argval = tuple(argval[0]) 232 else: 233 argval = () 234 out = stararg_handler(i, param, argval) 235 236 ba.arguments[name] = out 237 elif name in ba.arguments: 238 # Non-stararg, present 239 ba.arguments[name] = normal_handler(i, param, ba.arguments[name]) 240 else: 241 # Non-stararg, omitted 242 assert default is not param.empty 243 ba.arguments[name] = default_handler(i, param, default) 244 # Collect args in the right order 245 args = tuple(ba.arguments[param.name] 246 for param in pysig.parameters.values()) 247 return args 248 249 250class FunctionTemplate(ABC): 251 # Set to true to disable unsafe cast. 252 # subclass overide-able 253 unsafe_casting = True 254 # Set to true to require exact match without casting. 255 # subclass overide-able 256 exact_match_required = False 257 # Set to true to prefer literal arguments. 258 # Useful for definitions that specialize on literal but also support 259 # non-literals. 260 # subclass overide-able 261 prefer_literal = False 262 263 def __init__(self, context): 264 self.context = context 265 266 def _select(self, cases, args, kws): 267 options = { 268 'unsafe_casting': self.unsafe_casting, 269 'exact_match_required': self.exact_match_required, 270 } 271 selected = self.context.resolve_overload(self.key, cases, args, kws, 272 **options) 273 return selected 274 275 def get_impl_key(self, sig): 276 """ 277 Return the key for looking up the implementation for the given 278 signature on the target context. 279 """ 280 # Lookup the key on the class, to avoid binding it with `self`. 281 key = type(self).key 282 # On Python 2, we must also take care about unbound methods 283 if isinstance(key, MethodType): 284 assert key.im_self is None 285 key = key.im_func 286 return key 287 288 @classmethod 289 def get_source_code_info(cls, impl): 290 """ 291 Gets the source information about function impl. 292 Returns: 293 294 code - str: source code as a string 295 firstlineno - int: the first line number of the function impl 296 path - str: the path to file containing impl 297 298 if any of the above are not available something generic is returned 299 """ 300 try: 301 code, firstlineno = inspect.getsourcelines(impl) 302 except OSError: # missing source, probably a string 303 code = "None available (built from string?)" 304 firstlineno = 0 305 path = inspect.getsourcefile(impl) 306 if path is None: 307 path = "<unknown> (built from string?)" 308 return code, firstlineno, path 309 310 @abstractmethod 311 def get_template_info(self): 312 """ 313 Returns a dictionary with information specific to the template that will 314 govern how error messages are displayed to users. The dictionary must 315 be of the form: 316 info = { 317 'kind': "unknown", # str: The kind of template, e.g. "Overload" 318 'name': "unknown", # str: The name of the source function 319 'sig': "unknown", # str: The signature(s) of the source function 320 'filename': "unknown", # str: The filename of the source function 321 'lines': ("start", "end"), # tuple(int, int): The start and 322 end line of the source function. 323 'docstring': "unknown" # str: The docstring of the source function 324 } 325 """ 326 pass 327 328 def __str__(self): 329 info = self.get_template_info() 330 srcinfo = f"{info['filename']}:{info['lines'][0]}" 331 return f"<{self.__class__.__name__} {srcinfo}>" 332 333 __repr__ = __str__ 334 335 336class AbstractTemplate(FunctionTemplate): 337 """ 338 Defines method ``generic(self, args, kws)`` which compute a possible 339 signature base on input types. The signature does not have to match the 340 input types. It is compared against the input types afterwards. 341 """ 342 343 def apply(self, args, kws): 344 generic = getattr(self, "generic") 345 sig = generic(args, kws) 346 # Enforce that *generic()* must return None or Signature 347 if sig is not None: 348 if not isinstance(sig, Signature): 349 raise AssertionError( 350 "generic() must return a Signature or None. " 351 "{} returned {}".format(generic, type(sig)), 352 ) 353 354 # Unpack optional type if no matching signature 355 if not sig and any(isinstance(x, types.Optional) for x in args): 356 def unpack_opt(x): 357 if isinstance(x, types.Optional): 358 return x.type 359 else: 360 return x 361 362 args = list(map(unpack_opt, args)) 363 assert not kws # Not supported yet 364 sig = generic(args, kws) 365 366 return sig 367 368 def get_template_info(self): 369 impl = getattr(self, "generic") 370 basepath = os.path.dirname(os.path.dirname(numba.__file__)) 371 372 code, firstlineno, path = self.get_source_code_info(impl) 373 sig = str(utils.pysignature(impl)) 374 info = { 375 'kind': "overload", 376 'name': getattr(impl, '__qualname__', impl.__name__), 377 'sig': sig, 378 'filename': utils.safe_relpath(path, start=basepath), 379 'lines': (firstlineno, firstlineno + len(code) - 1), 380 'docstring': impl.__doc__ 381 } 382 return info 383 384 385class CallableTemplate(FunctionTemplate): 386 """ 387 Base class for a template defining a ``generic(self)`` method 388 returning a callable to be called with the actual ``*args`` and 389 ``**kwargs`` representing the call signature. The callable has 390 to return a return type, a full signature, or None. The signature 391 does not have to match the input types. It is compared against the 392 input types afterwards. 393 """ 394 recvr = None 395 396 def apply(self, args, kws): 397 generic = getattr(self, "generic") 398 typer = generic() 399 sig = typer(*args, **kws) 400 401 # Unpack optional type if no matching signature 402 if sig is None: 403 if any(isinstance(x, types.Optional) for x in args): 404 def unpack_opt(x): 405 if isinstance(x, types.Optional): 406 return x.type 407 else: 408 return x 409 410 args = list(map(unpack_opt, args)) 411 sig = typer(*args, **kws) 412 if sig is None: 413 return 414 415 # Get the pysig 416 try: 417 pysig = typer.pysig 418 except AttributeError: 419 pysig = utils.pysignature(typer) 420 421 # Fold any keyword arguments 422 bound = pysig.bind(*args, **kws) 423 if bound.kwargs: 424 raise TypingError("unsupported call signature") 425 if not isinstance(sig, Signature): 426 # If not a signature, `sig` is assumed to be the return type 427 if not isinstance(sig, types.Type): 428 raise TypeError("invalid return type for callable template: " 429 "got %r" % (sig,)) 430 sig = signature(sig, *bound.args) 431 if self.recvr is not None: 432 sig = sig.replace(recvr=self.recvr) 433 # Hack any omitted parameters out of the typer's pysig, 434 # as lowering expects an exact match between formal signature 435 # and actual args. 436 if len(bound.args) < len(pysig.parameters): 437 parameters = list(pysig.parameters.values())[:len(bound.args)] 438 pysig = pysig.replace(parameters=parameters) 439 sig = sig.replace(pysig=pysig) 440 cases = [sig] 441 return self._select(cases, bound.args, bound.kwargs) 442 443 def get_template_info(self): 444 impl = getattr(self, "generic") 445 basepath = os.path.dirname(os.path.dirname(numba.__file__)) 446 code, firstlineno, path = self.get_source_code_info(impl) 447 sig = str(utils.pysignature(impl)) 448 info = { 449 'kind': "overload", 450 'name': getattr(self.key, '__name__', 451 getattr(impl, '__qualname__', impl.__name__),), 452 'sig': sig, 453 'filename': utils.safe_relpath(path, start=basepath), 454 'lines': (firstlineno, firstlineno + len(code) - 1), 455 'docstring': impl.__doc__ 456 } 457 return info 458 459 460class ConcreteTemplate(FunctionTemplate): 461 """ 462 Defines attributes "cases" as a list of signature to match against the 463 given input types. 464 """ 465 466 def apply(self, args, kws): 467 cases = getattr(self, 'cases') 468 return self._select(cases, args, kws) 469 470 def get_template_info(self): 471 import operator 472 name = getattr(self.key, '__name__', "unknown") 473 op_func = getattr(operator, name, None) 474 475 kind = "Type restricted function" 476 if op_func is not None: 477 if self.key is op_func: 478 kind = "operator overload" 479 info = { 480 'kind': kind, 481 'name': name, 482 'sig': "unknown", 483 'filename': "unknown", 484 'lines': ("unknown", "unknown"), 485 'docstring': "unknown" 486 } 487 return info 488 489 490class _EmptyImplementationEntry(InternalError): 491 def __init__(self, reason): 492 super(_EmptyImplementationEntry, self).__init__( 493 "_EmptyImplementationEntry({!r})".format(reason), 494 ) 495 496 497class _OverloadFunctionTemplate(AbstractTemplate): 498 """ 499 A base class of templates for overload functions. 500 """ 501 502 def _validate_sigs(self, typing_func, impl_func): 503 # check that the impl func and the typing func have the same signature! 504 typing_sig = utils.pysignature(typing_func) 505 impl_sig = utils.pysignature(impl_func) 506 # the typing signature is considered golden and must be adhered to by 507 # the implementation... 508 # Things that are valid: 509 # 1. args match exactly 510 # 2. kwargs match exactly in name and default value 511 # 3. Use of *args in the same location by the same name in both typing 512 # and implementation signature 513 # 4. Use of *args in the implementation signature to consume any number 514 # of arguments in the typing signature. 515 # Things that are invalid: 516 # 5. Use of *args in the typing signature that is not replicated 517 # in the implementing signature 518 # 6. Use of **kwargs 519 520 def get_args_kwargs(sig): 521 kws = [] 522 args = [] 523 pos_arg = None 524 for x in sig.parameters.values(): 525 if x.default == utils.pyParameter.empty: 526 args.append(x) 527 if x.kind == utils.pyParameter.VAR_POSITIONAL: 528 pos_arg = x 529 elif x.kind == utils.pyParameter.VAR_KEYWORD: 530 msg = ("The use of VAR_KEYWORD (e.g. **kwargs) is " 531 "unsupported. (offending argument name is '%s')") 532 raise InternalError(msg % x) 533 else: 534 kws.append(x) 535 return args, kws, pos_arg 536 537 ty_args, ty_kws, ty_pos = get_args_kwargs(typing_sig) 538 im_args, im_kws, im_pos = get_args_kwargs(impl_sig) 539 540 sig_fmt = ("Typing signature: %s\n" 541 "Implementation signature: %s") 542 sig_str = sig_fmt % (typing_sig, impl_sig) 543 544 err_prefix = "Typing and implementation arguments differ in " 545 546 a = ty_args 547 b = im_args 548 if ty_pos: 549 if not im_pos: 550 # case 5. described above 551 msg = ("VAR_POSITIONAL (e.g. *args) argument kind (offending " 552 "argument name is '%s') found in the typing function " 553 "signature, but is not in the implementing function " 554 "signature.\n%s") % (ty_pos, sig_str) 555 raise InternalError(msg) 556 else: 557 if im_pos: 558 # no *args in typing but there's a *args in the implementation 559 # this is case 4. described above 560 b = im_args[:im_args.index(im_pos)] 561 try: 562 a = ty_args[:ty_args.index(b[-1]) + 1] 563 except ValueError: 564 # there's no b[-1] arg name in the ty_args, something is 565 # very wrong, we can't work out a diff (*args consumes 566 # unknown quantity of args) so just report first error 567 specialized = "argument names.\n%s\nFirst difference: '%s'" 568 msg = err_prefix + specialized % (sig_str, b[-1]) 569 raise InternalError(msg) 570 571 def gen_diff(typing, implementing): 572 diff = set(typing) ^ set(implementing) 573 return "Difference: %s" % diff 574 575 if a != b: 576 specialized = "argument names.\n%s\n%s" % (sig_str, gen_diff(a, b)) 577 raise InternalError(err_prefix + specialized) 578 579 # ensure kwargs are the same 580 ty = [x.name for x in ty_kws] 581 im = [x.name for x in im_kws] 582 if ty != im: 583 specialized = "keyword argument names.\n%s\n%s" 584 msg = err_prefix + specialized % (sig_str, gen_diff(ty_kws, im_kws)) 585 raise InternalError(msg) 586 same = [x.default for x in ty_kws] == [x.default for x in im_kws] 587 if not same: 588 specialized = "keyword argument default values.\n%s\n%s" 589 msg = err_prefix + specialized % (sig_str, gen_diff(ty_kws, im_kws)) 590 raise InternalError(msg) 591 592 def generic(self, args, kws): 593 """ 594 Type the overloaded function by compiling the appropriate 595 implementation for the given args. 596 """ 597 disp, new_args = self._get_impl(args, kws) 598 if disp is None: 599 return 600 # Compile and type it for the given types 601 disp_type = types.Dispatcher(disp) 602 # Store the compiled overload for use in the lowering phase if there's 603 # no inlining required (else functions are being compiled which will 604 # never be used as they are inlined) 605 if not self._inline.is_never_inline: 606 # need to run the compiler front end up to type inference to compute 607 # a signature 608 from numba.core import typed_passes, compiler 609 from numba.core.inline_closurecall import InlineWorker 610 fcomp = disp._compiler 611 flags = compiler.Flags() 612 613 # Updating these causes problems?! 614 #fcomp.targetdescr.options.parse_as_flags(flags, 615 # fcomp.targetoptions) 616 #flags = fcomp._customize_flags(flags) 617 618 # spoof a compiler pipline like the one that will be in use 619 tyctx = fcomp.targetdescr.typing_context 620 tgctx = fcomp.targetdescr.target_context 621 compiler_inst = fcomp.pipeline_class(tyctx, tgctx, None, None, None, 622 flags, None, ) 623 inline_worker = InlineWorker(tyctx, tgctx, fcomp.locals, 624 compiler_inst, flags, None,) 625 626 # If the inlinee contains something to trigger literal arg dispatch 627 # then the pipeline call will unconditionally fail due to a raised 628 # ForceLiteralArg exception. Therefore `resolve` is run first, as 629 # type resolution must occur at some point, this will hit any 630 # `literally` calls and because it's going via the dispatcher will 631 # handle them correctly i.e. ForceLiteralArg propagates. This having 632 # the desired effect of ensuring the pipeline call is only made in 633 # situations that will succeed. For context see #5887. 634 resolve = disp_type.dispatcher.get_call_template 635 template, pysig, folded_args, kws = resolve(new_args, kws) 636 ir = inline_worker.run_untyped_passes(disp_type.dispatcher.py_func) 637 638 typemap, return_type, calltypes = typed_passes.type_inference_stage( 639 self.context, ir, folded_args, None) 640 sig = Signature(return_type, folded_args, None) 641 # this stores a load of info for the cost model function if supplied 642 # it by default is None 643 self._inline_overloads[sig.args] = {'folded_args': folded_args} 644 # this stores the compiled overloads, if there's no compiled 645 # overload available i.e. function is always inlined, the key still 646 # needs to exist for type resolution 647 648 # NOTE: If lowering is failing on a `_EmptyImplementationEntry`, 649 # the inliner has failed to inline this entry corretly. 650 impl_init = _EmptyImplementationEntry('always inlined') 651 self._compiled_overloads[sig.args] = impl_init 652 if not self._inline.is_always_inline: 653 # this branch is here because a user has supplied a function to 654 # determine whether to inline or not. As a result both compiled 655 # function and inliner info needed, delaying the computation of 656 # this leads to an internal state mess at present. TODO: Fix! 657 sig = disp_type.get_call_type(self.context, new_args, kws) 658 self._compiled_overloads[sig.args] = disp_type.get_overload(sig) 659 # store the inliner information, it's used later in the cost 660 # model function call 661 iinfo = _inline_info(ir, typemap, calltypes, sig) 662 self._inline_overloads[sig.args] = {'folded_args': folded_args, 663 'iinfo': iinfo} 664 else: 665 sig = disp_type.get_call_type(self.context, new_args, kws) 666 self._compiled_overloads[sig.args] = disp_type.get_overload(sig) 667 return sig 668 669 def _get_impl(self, args, kws): 670 """Get implementation given the argument types. 671 672 Returning a Dispatcher object. The Dispatcher object is cached 673 internally in `self._impl_cache`. 674 """ 675 cache_key = self.context, tuple(args), tuple(kws.items()) 676 try: 677 impl, args = self._impl_cache[cache_key] 678 except KeyError: 679 impl, args = self._build_impl(cache_key, args, kws) 680 return impl, args 681 682 def _build_impl(self, cache_key, args, kws): 683 """Build and cache the implementation. 684 685 Given the positional (`args`) and keyword arguments (`kws`), obtains 686 the `overload` implementation and wrap it in a Dispatcher object. 687 The expected argument types are returned for use by type-inference. 688 The expected argument types are only different from the given argument 689 types if there is an imprecise type in the given argument types. 690 691 Parameters 692 ---------- 693 cache_key : hashable 694 The key used for caching the implementation. 695 args : Tuple[Type] 696 Types of positional argument. 697 kws : Dict[Type] 698 Types of keyword argument. 699 700 Returns 701 ------- 702 disp, args : 703 On success, returns `(Dispatcher, Tuple[Type])`. 704 On failure, returns `(None, None)`. 705 706 """ 707 from numba import jit 708 709 # Get the overload implementation for the given types 710 ovf_result = self._overload_func(*args, **kws) 711 if ovf_result is None: 712 # No implementation => fail typing 713 self._impl_cache[cache_key] = None, None 714 return None, None 715 elif isinstance(ovf_result, tuple): 716 # The implementation returned a signature that the type-inferencer 717 # should be using. 718 sig, pyfunc = ovf_result 719 args = sig.args 720 kws = {} 721 cache_key = None # don't cache 722 else: 723 # Regular case 724 pyfunc = ovf_result 725 726 # Check type of pyfunc 727 if not isinstance(pyfunc, FunctionType): 728 msg = ("Implementator function returned by `@overload` " 729 "has an unexpected type. Got {}") 730 raise AssertionError(msg.format(pyfunc)) 731 732 # check that the typing and impl sigs match up 733 if self._strict: 734 self._validate_sigs(self._overload_func, pyfunc) 735 # Make dispatcher 736 jitdecor = jit(nopython=True, **self._jit_options) 737 disp = jitdecor(pyfunc) 738 # Make sure that the implementation can be fully compiled 739 disp_type = types.Dispatcher(disp) 740 disp_type.get_call_type(self.context, args, kws) 741 if cache_key is not None: 742 self._impl_cache[cache_key] = disp, args 743 return disp, args 744 745 def get_impl_key(self, sig): 746 """ 747 Return the key for looking up the implementation for the given 748 signature on the target context. 749 """ 750 return self._compiled_overloads[sig.args] 751 752 @classmethod 753 def get_source_info(cls): 754 """Return a dictionary with information about the source code of the 755 implementation. 756 757 Returns 758 ------- 759 info : dict 760 - "kind" : str 761 The implementation kind. 762 - "name" : str 763 The name of the function that provided the definition. 764 - "sig" : str 765 The formatted signature of the function. 766 - "filename" : str 767 The name of the source file. 768 - "lines": tuple (int, int) 769 First and list line number. 770 - "docstring": str 771 The docstring of the definition. 772 """ 773 basepath = os.path.dirname(os.path.dirname(numba.__file__)) 774 impl = cls._overload_func 775 code, firstlineno, path = cls.get_source_code_info(impl) 776 sig = str(utils.pysignature(impl)) 777 info = { 778 'kind': "overload", 779 'name': getattr(impl, '__qualname__', impl.__name__), 780 'sig': sig, 781 'filename': utils.safe_relpath(path, start=basepath), 782 'lines': (firstlineno, firstlineno + len(code) - 1), 783 'docstring': impl.__doc__ 784 } 785 return info 786 787 def get_template_info(self): 788 basepath = os.path.dirname(os.path.dirname(numba.__file__)) 789 impl = self._overload_func 790 code, firstlineno, path = self.get_source_code_info(impl) 791 sig = str(utils.pysignature(impl)) 792 info = { 793 'kind': "overload", 794 'name': getattr(impl, '__qualname__', impl.__name__), 795 'sig': sig, 796 'filename': utils.safe_relpath(path, start=basepath), 797 'lines': (firstlineno, firstlineno + len(code) - 1), 798 'docstring': impl.__doc__ 799 } 800 return info 801 802 803def make_overload_template(func, overload_func, jit_options, strict, 804 inline, prefer_literal=False): 805 """ 806 Make a template class for function *func* overloaded by *overload_func*. 807 Compiler options are passed as a dictionary to *jit_options*. 808 """ 809 func_name = getattr(func, '__name__', str(func)) 810 name = "OverloadTemplate_%s" % (func_name,) 811 base = _OverloadFunctionTemplate 812 dct = dict(key=func, _overload_func=staticmethod(overload_func), 813 _impl_cache={}, _compiled_overloads={}, _jit_options=jit_options, 814 _strict=strict, _inline=staticmethod(InlineOptions(inline)), 815 _inline_overloads={}, prefer_literal=prefer_literal) 816 return type(base)(name, (base,), dct) 817 818 819class _IntrinsicTemplate(AbstractTemplate): 820 """ 821 A base class of templates for intrinsic definition 822 """ 823 824 def generic(self, args, kws): 825 """ 826 Type the intrinsic by the arguments. 827 """ 828 from numba.core.imputils import lower_builtin 829 830 cache_key = self.context, args, tuple(kws.items()) 831 try: 832 return self._impl_cache[cache_key] 833 except KeyError: 834 result = self._definition_func(self.context, *args, **kws) 835 if result is None: 836 return 837 [sig, imp] = result 838 pysig = utils.pysignature(self._definition_func) 839 # omit context argument from user function 840 parameters = list(pysig.parameters.values())[1:] 841 sig = sig.replace(pysig=pysig.replace(parameters=parameters)) 842 self._impl_cache[cache_key] = sig 843 self._overload_cache[sig.args] = imp 844 # register the lowering 845 lower_builtin(imp, *sig.args)(imp) 846 return sig 847 848 def get_impl_key(self, sig): 849 """ 850 Return the key for looking up the implementation for the given 851 signature on the target context. 852 """ 853 return self._overload_cache[sig.args] 854 855 def get_template_info(self): 856 basepath = os.path.dirname(os.path.dirname(numba.__file__)) 857 impl = self._definition_func 858 code, firstlineno, path = self.get_source_code_info(impl) 859 sig = str(utils.pysignature(impl)) 860 info = { 861 'kind': "intrinsic", 862 'name': getattr(impl, '__qualname__', impl.__name__), 863 'sig': sig, 864 'filename': utils.safe_relpath(path, start=basepath), 865 'lines': (firstlineno, firstlineno + len(code) - 1), 866 'docstring': impl.__doc__ 867 } 868 return info 869 870 871def make_intrinsic_template(handle, defn, name): 872 """ 873 Make a template class for a intrinsic handle *handle* defined by the 874 function *defn*. The *name* is used for naming the new template class. 875 """ 876 base = _IntrinsicTemplate 877 name = "_IntrinsicTemplate_%s" % (name) 878 dct = dict(key=handle, _definition_func=staticmethod(defn), 879 _impl_cache={}, _overload_cache={}) 880 return type(base)(name, (base,), dct) 881 882 883class AttributeTemplate(object): 884 _initialized = False 885 886 def __init__(self, context): 887 self._lazy_class_init() 888 self.context = context 889 890 def resolve(self, value, attr): 891 return self._resolve(value, attr) 892 893 @classmethod 894 def _lazy_class_init(cls): 895 if not cls._initialized: 896 cls.do_class_init() 897 cls._initialized = True 898 899 @classmethod 900 def do_class_init(cls): 901 """ 902 Class-wide initialization. Can be overridden by subclasses to 903 register permanent typing or target hooks. 904 """ 905 906 def _resolve(self, value, attr): 907 fn = getattr(self, "resolve_%s" % attr, None) 908 if fn is None: 909 fn = self.generic_resolve 910 if fn is NotImplemented: 911 if isinstance(value, types.Module): 912 return self.context.resolve_module_constants(value, attr) 913 else: 914 return None 915 else: 916 return fn(value, attr) 917 else: 918 return fn(value) 919 920 generic_resolve = NotImplemented 921 922 923class _OverloadAttributeTemplate(AttributeTemplate): 924 """ 925 A base class of templates for @overload_attribute functions. 926 """ 927 is_method = False 928 929 def __init__(self, context): 930 super(_OverloadAttributeTemplate, self).__init__(context) 931 self.context = context 932 933 @classmethod 934 def do_class_init(cls): 935 """ 936 Register attribute implementation. 937 """ 938 from numba.core.imputils import lower_getattr 939 attr = cls._attr 940 941 @lower_getattr(cls.key, attr) 942 def getattr_impl(context, builder, typ, value): 943 typingctx = context.typing_context 944 fnty = cls._get_function_type(typingctx, typ) 945 sig = cls._get_signature(typingctx, fnty, (typ,), {}) 946 call = context.get_function(fnty, sig) 947 return call(builder, (value,)) 948 949 def _resolve(self, typ, attr): 950 if self._attr != attr: 951 return None 952 fnty = self._get_function_type(self.context, typ) 953 sig = self._get_signature(self.context, fnty, (typ,), {}) 954 # There should only be one template 955 for template in fnty.templates: 956 self._inline_overloads.update(template._inline_overloads) 957 return sig.return_type 958 959 @classmethod 960 def _get_signature(cls, typingctx, fnty, args, kws): 961 sig = fnty.get_call_type(typingctx, args, kws) 962 sig = sig.replace(pysig=utils.pysignature(cls._overload_func)) 963 return sig 964 965 @classmethod 966 def _get_function_type(cls, typingctx, typ): 967 return typingctx.resolve_value_type(cls._overload_func) 968 969 970class _OverloadMethodTemplate(_OverloadAttributeTemplate): 971 """ 972 A base class of templates for @overload_method functions. 973 """ 974 is_method = True 975 976 @classmethod 977 def do_class_init(cls): 978 """ 979 Register generic method implementation. 980 """ 981 from numba.core.imputils import lower_builtin 982 attr = cls._attr 983 984 @lower_builtin((cls.key, attr), cls.key, types.VarArg(types.Any)) 985 def method_impl(context, builder, sig, args): 986 typ = sig.args[0] 987 typing_context = context.typing_context 988 fnty = cls._get_function_type(typing_context, typ) 989 sig = cls._get_signature(typing_context, fnty, sig.args, {}) 990 call = context.get_function(fnty, sig) 991 # Link dependent library 992 context.add_linking_libs(getattr(call, 'libs', ())) 993 return call(builder, args) 994 995 def _resolve(self, typ, attr): 996 if self._attr != attr: 997 return None 998 999 assert isinstance(typ, self.key) 1000 1001 class MethodTemplate(AbstractTemplate): 1002 key = (self.key, attr) 1003 _inline = self._inline 1004 _overload_func = staticmethod(self._overload_func) 1005 _inline_overloads = self._inline_overloads 1006 prefer_literal = self.prefer_literal 1007 1008 def generic(_, args, kws): 1009 args = (typ,) + tuple(args) 1010 fnty = self._get_function_type(self.context, typ) 1011 sig = self._get_signature(self.context, fnty, args, kws) 1012 sig = sig.replace(pysig=utils.pysignature(self._overload_func)) 1013 for template in fnty.templates: 1014 self._inline_overloads.update(template._inline_overloads) 1015 if sig is not None: 1016 return sig.as_method() 1017 1018 return types.BoundFunction(MethodTemplate, typ) 1019 1020 1021def make_overload_attribute_template(typ, attr, overload_func, inline, 1022 prefer_literal=False, 1023 base=_OverloadAttributeTemplate): 1024 """ 1025 Make a template class for attribute *attr* of *typ* overloaded by 1026 *overload_func*. 1027 """ 1028 assert isinstance(typ, types.Type) or issubclass(typ, types.Type) 1029 name = "OverloadAttributeTemplate_%s_%s" % (typ, attr) 1030 # Note the implementation cache is subclass-specific 1031 dct = dict(key=typ, _attr=attr, _impl_cache={}, 1032 _inline=staticmethod(InlineOptions(inline)), 1033 _inline_overloads={}, 1034 _overload_func=staticmethod(overload_func), 1035 prefer_literal=prefer_literal, 1036 ) 1037 obj = type(base)(name, (base,), dct) 1038 return obj 1039 1040 1041def make_overload_method_template(typ, attr, overload_func, inline, 1042 prefer_literal=False): 1043 """ 1044 Make a template class for method *attr* of *typ* overloaded by 1045 *overload_func*. 1046 """ 1047 return make_overload_attribute_template( 1048 typ, attr, overload_func, inline=inline, 1049 base=_OverloadMethodTemplate, prefer_literal=prefer_literal, 1050 ) 1051 1052 1053def bound_function(template_key): 1054 """ 1055 Wrap an AttributeTemplate resolve_* method to allow it to 1056 resolve an instance method's signature rather than a instance attribute. 1057 The wrapped method must return the resolved method's signature 1058 according to the given self type, args, and keywords. 1059 1060 It is used thusly: 1061 1062 class ComplexAttributes(AttributeTemplate): 1063 @bound_function("complex.conjugate") 1064 def resolve_conjugate(self, ty, args, kwds): 1065 return ty 1066 1067 *template_key* (e.g. "complex.conjugate" above) will be used by the 1068 target to look up the method's implementation, as a regular function. 1069 """ 1070 def wrapper(method_resolver): 1071 @functools.wraps(method_resolver) 1072 def attribute_resolver(self, ty): 1073 class MethodTemplate(AbstractTemplate): 1074 key = template_key 1075 1076 def generic(_, args, kws): 1077 sig = method_resolver(self, ty, args, kws) 1078 if sig is not None and sig.recvr is None: 1079 sig = sig.replace(recvr=ty) 1080 return sig 1081 1082 return types.BoundFunction(MethodTemplate, ty) 1083 return attribute_resolver 1084 return wrapper 1085 1086 1087class MacroTemplate(object): 1088 pass 1089 1090 1091# ----------------------------- 1092 1093class Registry(object): 1094 """ 1095 A registry of typing declarations. The registry stores such declarations 1096 for functions, attributes and globals. 1097 """ 1098 1099 def __init__(self): 1100 self.functions = [] 1101 self.attributes = [] 1102 self.globals = [] 1103 1104 def register(self, item): 1105 assert issubclass(item, FunctionTemplate) 1106 self.functions.append(item) 1107 return item 1108 1109 def register_attr(self, item): 1110 assert issubclass(item, AttributeTemplate) 1111 self.attributes.append(item) 1112 return item 1113 1114 def register_global(self, val=None, typ=None, **kwargs): 1115 """ 1116 Register the typing of a global value. 1117 Functional usage with a Numba type:: 1118 register_global(value, typ) 1119 1120 Decorator usage with a template class:: 1121 @register_global(value, typing_key=None) 1122 class Template: 1123 ... 1124 """ 1125 if typ is not None: 1126 # register_global(val, typ) 1127 assert val is not None 1128 assert not kwargs 1129 self.globals.append((val, typ)) 1130 else: 1131 def decorate(cls, typing_key): 1132 class Template(cls): 1133 key = typing_key 1134 if callable(val): 1135 typ = types.Function(Template) 1136 else: 1137 raise TypeError("cannot infer type for global value %r") 1138 self.globals.append((val, typ)) 1139 return cls 1140 1141 # register_global(val, typing_key=None)(<template class>) 1142 assert val is not None 1143 typing_key = kwargs.pop('typing_key', val) 1144 assert not kwargs 1145 if typing_key is val: 1146 # Check the value is globally reachable, as it is going 1147 # to be used as the key. 1148 mod = sys.modules[val.__module__] 1149 if getattr(mod, val.__name__) is not val: 1150 raise ValueError("%r is not globally reachable as '%s.%s'" 1151 % (mod, val.__module__, val.__name__)) 1152 1153 def decorator(cls): 1154 return decorate(cls, typing_key) 1155 return decorator 1156 1157 1158class BaseRegistryLoader(object): 1159 """ 1160 An incremental loader for a registry. Each new call to 1161 new_registrations() will iterate over the not yet seen registrations. 1162 1163 The reason for this object is multiple: 1164 - there can be several contexts 1165 - each context wants to install all registrations 1166 - registrations can be added after the first installation, so contexts 1167 must be able to get the "new" installations 1168 1169 Therefore each context maintains its own loaders for each existing 1170 registry, without duplicating the registries themselves. 1171 """ 1172 1173 def __init__(self, registry): 1174 self._registrations = dict( 1175 (name, utils.stream_list(getattr(registry, name))) 1176 for name in self.registry_items) 1177 1178 def new_registrations(self, name): 1179 for item in next(self._registrations[name]): 1180 yield item 1181 1182 1183class RegistryLoader(BaseRegistryLoader): 1184 """ 1185 An incremental loader for a typing registry. 1186 """ 1187 registry_items = ('functions', 'attributes', 'globals') 1188 1189 1190builtin_registry = Registry() 1191infer = builtin_registry.register 1192infer_getattr = builtin_registry.register_attr 1193infer_global = builtin_registry.register_global 1194