10
10
instance of the callable class.
11
11
"""
12
12
13
- from typing import Optional , List , Tuple , Union , Dict
13
+ from typing import NamedTuple , Optional , List , Sequence , Tuple , Union , Dict
14
14
15
15
from mypy .nodes import (
16
16
ClassDef , FuncDef , OverloadedFuncDef , Decorator , Var , YieldFromExpr , AwaitExpr , YieldExpr ,
17
- FuncItem , LambdaExpr , SymbolNode , ARG_NAMED , ARG_NAMED_OPT
17
+ FuncItem , LambdaExpr , SymbolNode , ARG_NAMED , ARG_NAMED_OPT , TypeInfo
18
18
)
19
19
from mypy .types import CallableType , get_proper_type
20
20
28
28
)
29
29
from mypyc .ir .class_ir import ClassIR , NonExtClassInfo
30
30
from mypyc .primitives .generic_ops import py_setattr_op , next_raw_op , iter_op
31
- from mypyc .primitives .misc_ops import check_stop_op , yield_from_except_op , coro_op , send_op
31
+ from mypyc .primitives .misc_ops import (
32
+ check_stop_op , yield_from_except_op , coro_op , send_op , slow_isinstance_op
33
+ )
32
34
from mypyc .primitives .dict_ops import dict_set_item_op
33
35
from mypyc .common import SELF_NAME , LAMBDA_NAME , decorator_helper_name
34
36
from mypyc .sametype import is_same_method_signature
@@ -84,7 +86,10 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
84
86
decorated_func = load_decorated_func (builder , dec .func , func_reg )
85
87
builder .assign (get_func_target (builder , dec .func ), decorated_func , dec .func .line )
86
88
func_reg = decorated_func
87
- else :
89
+ # If the prebuild pass didn't put this function in the function to decorators map (for example
90
+ # if this is a registered singledispatch implementation with no other decorators), we should
91
+ # treat this function as a regular function, not a decorated function
92
+ elif dec .func in builder .fdefs_to_decorators :
88
93
# Obtain the the function name in order to construct the name of the helper function.
89
94
name = dec .func .fullname .split ('.' )[- 1 ]
90
95
helper_name = decorator_helper_name (name )
@@ -206,6 +211,7 @@ def c() -> None:
206
211
is_nested = fitem in builder .nested_fitems or isinstance (fitem , LambdaExpr )
207
212
contains_nested = fitem in builder .encapsulating_funcs .keys ()
208
213
is_decorated = fitem in builder .fdefs_to_decorators
214
+ is_singledispatch = fitem in builder .singledispatch_impls
209
215
in_non_ext = False
210
216
class_name = None
211
217
if cdef :
@@ -214,7 +220,8 @@ def c() -> None:
214
220
class_name = cdef .name
215
221
216
222
builder .enter (FuncInfo (fitem , name , class_name , gen_func_ns (builder ),
217
- is_nested , contains_nested , is_decorated , in_non_ext ))
223
+ is_nested , contains_nested , is_decorated , in_non_ext ,
224
+ is_singledispatch ))
218
225
219
226
# Functions that contain nested functions need an environment class to store variables that
220
227
# are free in their nested functions. Generator functions need an environment class to
@@ -247,6 +254,9 @@ def c() -> None:
247
254
if builder .fn_info .contains_nested and not builder .fn_info .is_generator :
248
255
finalize_env_class (builder )
249
256
257
+ if builder .fn_info .is_singledispatch :
258
+ add_singledispatch_registered_impls (builder )
259
+
250
260
builder .ret_types [- 1 ] = sig .ret_type
251
261
252
262
# Add all variables and functions that are declared/defined within this
@@ -628,6 +638,23 @@ def gen_glue(builder: IRBuilder, sig: FuncSignature, target: FuncIR,
628
638
return gen_glue_method (builder , sig , target , cls , base , fdef .line , do_py_ops )
629
639
630
640
641
+ class ArgInfo (NamedTuple ):
642
+ args : List [Value ]
643
+ arg_names : List [Optional [str ]]
644
+ arg_kinds : List [int ]
645
+
646
+
647
+ def get_args (builder : IRBuilder , rt_args : Sequence [RuntimeArg ], line : int ) -> ArgInfo :
648
+ # The environment operates on Vars, so we make some up
649
+ fake_vars = [(Var (arg .name ), arg .type ) for arg in rt_args ]
650
+ args = [builder .read (builder .add_local_reg (var , type , is_arg = True ), line )
651
+ for var , type in fake_vars ]
652
+ arg_names = [arg .name if arg .kind in (ARG_NAMED , ARG_NAMED_OPT ) else None
653
+ for arg in rt_args ]
654
+ arg_kinds = [concrete_arg_kind (arg .kind ) for arg in rt_args ]
655
+ return ArgInfo (args , arg_names , arg_kinds )
656
+
657
+
631
658
def gen_glue_method (builder : IRBuilder , sig : FuncSignature , target : FuncIR ,
632
659
cls : ClassIR , base : ClassIR , line : int ,
633
660
do_pycall : bool ,
@@ -664,13 +691,8 @@ def f(builder: IRBuilder, x: object) -> int: ...
664
691
if target .decl .kind == FUNC_NORMAL :
665
692
rt_args [0 ] = RuntimeArg (sig .args [0 ].name , RInstance (cls ))
666
693
667
- # The environment operates on Vars, so we make some up
668
- fake_vars = [(Var (arg .name ), arg .type ) for arg in rt_args ]
669
- args = [builder .read (builder .add_local_reg (var , type , is_arg = True ), line )
670
- for var , type in fake_vars ]
671
- arg_names = [arg .name if arg .kind in (ARG_NAMED , ARG_NAMED_OPT ) else None
672
- for arg in rt_args ]
673
- arg_kinds = [concrete_arg_kind (arg .kind ) for arg in rt_args ]
694
+ arg_info = get_args (builder , rt_args , line )
695
+ args , arg_kinds , arg_names = arg_info .args , arg_info .arg_kinds , arg_info .arg_names
674
696
675
697
if do_pycall :
676
698
retval = builder .builder .py_method_call (
@@ -739,3 +761,35 @@ def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget:
739
761
return builder .lookup (fdef )
740
762
741
763
return builder .add_local_reg (fdef , object_rprimitive )
764
+
765
+
766
+ def check_if_isinstance (builder : IRBuilder , obj : Value , typ : TypeInfo , line : int ) -> Value :
767
+ if typ in builder .mapper .type_to_ir :
768
+ class_ir = builder .mapper .type_to_ir [typ ]
769
+ return builder .builder .isinstance_native (obj , class_ir , line )
770
+ else :
771
+ class_obj = builder .load_module_attr_by_fullname (typ .fullname , line )
772
+ return builder .call_c (slow_isinstance_op , [obj , class_obj ], line )
773
+
774
+
775
+ def add_singledispatch_registered_impls (builder : IRBuilder ) -> None :
776
+ fitem = builder .fn_info .fitem
777
+ assert isinstance (fitem , FuncDef )
778
+ impls = builder .singledispatch_impls [fitem ]
779
+ line = fitem .line
780
+ current_func_decl = builder .mapper .func_to_decl [fitem ]
781
+ arg_info = get_args (builder , current_func_decl .sig .args , line )
782
+ for dispatch_type , impl in impls :
783
+ func_decl = builder .mapper .func_to_decl [impl ]
784
+ call_impl , next_impl = BasicBlock (), BasicBlock ()
785
+ should_call_impl = check_if_isinstance (builder , arg_info .args [0 ], dispatch_type , line )
786
+ builder .add_bool_branch (should_call_impl , call_impl , next_impl )
787
+
788
+ # Call the registered implementation
789
+ builder .activate_block (call_impl )
790
+
791
+ ret_val = builder .builder .call (
792
+ func_decl , arg_info .args , arg_info .arg_kinds , arg_info .arg_names , line
793
+ )
794
+ builder .nonlocal_control [- 1 ].gen_return (builder , ret_val , line )
795
+ builder .activate_block (next_impl )
0 commit comments