38
38
get_proper_types , is_literal_type , TypeAliasType )
39
39
from mypy .sametypes import is_same_type
40
40
from mypy .messages import (
41
- MessageBuilder , make_inferred_type_note , append_invariance_notes ,
41
+ MessageBuilder , make_inferred_type_note , append_invariance_notes , pretty_seq ,
42
42
format_type , format_type_bare , format_type_distinctly , SUGGESTED_TEST_FIXTURES
43
43
)
44
44
import mypy .checkexpr
63
63
from mypy .maptype import map_instance_to_supertype
64
64
from mypy .typevars import fill_typevars , has_no_typevars , fill_typevars_with_any
65
65
from mypy .semanal import set_callable_name , refers_to_fullname
66
- from mypy .mro import calculate_mro
66
+ from mypy .mro import calculate_mro , MroError
67
67
from mypy .erasetype import erase_typevars , remove_instance_last_known_values , erase_type
68
68
from mypy .expandtype import expand_type , expand_type_by_instance
69
69
from mypy .visitor import NodeVisitor
@@ -1963,13 +1963,15 @@ def visit_block(self, b: Block) -> None:
1963
1963
return
1964
1964
for s in b .body :
1965
1965
if self .binder .is_unreachable ():
1966
- if (self .options .warn_unreachable
1967
- and not self .binder .is_unreachable_warning_suppressed ()
1968
- and not self .is_raising_or_empty (s )):
1966
+ if self .should_report_unreachable_issues () and not self .is_raising_or_empty (s ):
1969
1967
self .msg .unreachable_statement (s )
1970
1968
break
1971
1969
self .accept (s )
1972
1970
1971
+ def should_report_unreachable_issues (self ) -> bool :
1972
+ return (self .options .warn_unreachable
1973
+ and not self .binder .is_unreachable_warning_suppressed ())
1974
+
1973
1975
def is_raising_or_empty (self , s : Statement ) -> bool :
1974
1976
"""Returns 'true' if the given statement either throws an error of some kind
1975
1977
or is a no-op.
@@ -3636,6 +3638,100 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None:
3636
3638
self .binder .handle_continue ()
3637
3639
return None
3638
3640
3641
+ def make_fake_typeinfo (self ,
3642
+ curr_module_fullname : str ,
3643
+ class_gen_name : str ,
3644
+ class_short_name : str ,
3645
+ bases : List [Instance ],
3646
+ ) -> Tuple [ClassDef , TypeInfo ]:
3647
+ # Build the fake ClassDef and TypeInfo together.
3648
+ # The ClassDef is full of lies and doesn't actually contain a body.
3649
+ # Use format_bare to generate a nice name for error messages.
3650
+ # We skip fully filling out a handful of TypeInfo fields because they
3651
+ # should be irrelevant for a generated type like this:
3652
+ # is_protocol, protocol_members, is_abstract
3653
+ cdef = ClassDef (class_short_name , Block ([]))
3654
+ cdef .fullname = curr_module_fullname + '.' + class_gen_name
3655
+ info = TypeInfo (SymbolTable (), cdef , curr_module_fullname )
3656
+ cdef .info = info
3657
+ info .bases = bases
3658
+ calculate_mro (info )
3659
+ info .calculate_metaclass_type ()
3660
+ return cdef , info
3661
+
3662
+ def intersect_instances (self ,
3663
+ instances : Sequence [Instance ],
3664
+ ctx : Context ,
3665
+ ) -> Optional [Instance ]:
3666
+ """Try creating an ad-hoc intersection of the given instances.
3667
+
3668
+ Note that this function does *not* try and create a full-fledged
3669
+ intersection type. Instead, it returns an instance of a new ad-hoc
3670
+ subclass of the given instances.
3671
+
3672
+ This is mainly useful when you need a way of representing some
3673
+ theoretical subclass of the instances the user may be trying to use
3674
+ the generated intersection can serve as a placeholder.
3675
+
3676
+ This function will create a fresh subclass every time you call it,
3677
+ even if you pass in the exact same arguments. So this means calling
3678
+ `self.intersect_intersection([inst_1, inst_2], ctx)` twice will result
3679
+ in instances of two distinct subclasses of inst_1 and inst_2.
3680
+
3681
+ This is by design: we want each ad-hoc intersection to be unique since
3682
+ they're supposed represent some other unknown subclass.
3683
+
3684
+ Returns None if creating the subclass is impossible (e.g. due to
3685
+ MRO errors or incompatible signatures). If we do successfully create
3686
+ a subclass, its TypeInfo will automatically be added to the global scope.
3687
+ """
3688
+ curr_module = self .scope .stack [0 ]
3689
+ assert isinstance (curr_module , MypyFile )
3690
+
3691
+ base_classes = []
3692
+ formatted_names = []
3693
+ for inst in instances :
3694
+ expanded = [inst ]
3695
+ if inst .type .is_intersection :
3696
+ expanded = inst .type .bases
3697
+
3698
+ for expanded_inst in expanded :
3699
+ base_classes .append (expanded_inst )
3700
+ formatted_names .append (format_type_bare (expanded_inst ))
3701
+
3702
+ pretty_names_list = pretty_seq (format_type_distinctly (* base_classes , bare = True ), "and" )
3703
+ short_name = '<subclass of {}>' .format (pretty_names_list )
3704
+ full_name = gen_unique_name (short_name , curr_module .names )
3705
+
3706
+ old_msg = self .msg
3707
+ new_msg = self .msg .clean_copy ()
3708
+ self .msg = new_msg
3709
+ try :
3710
+ cdef , info = self .make_fake_typeinfo (
3711
+ curr_module .fullname ,
3712
+ full_name ,
3713
+ short_name ,
3714
+ base_classes ,
3715
+ )
3716
+ self .check_multiple_inheritance (info )
3717
+ info .is_intersection = True
3718
+ except MroError :
3719
+ if self .should_report_unreachable_issues ():
3720
+ old_msg .impossible_intersection (
3721
+ pretty_names_list , "inconsistent method resolution order" , ctx )
3722
+ return None
3723
+ finally :
3724
+ self .msg = old_msg
3725
+
3726
+ if new_msg .is_errors ():
3727
+ if self .should_report_unreachable_issues ():
3728
+ self .msg .impossible_intersection (
3729
+ pretty_names_list , "incompatible method signatures" , ctx )
3730
+ return None
3731
+
3732
+ curr_module .names [full_name ] = SymbolTableNode (GDEF , info )
3733
+ return Instance (info , [])
3734
+
3639
3735
def intersect_instance_callable (self , typ : Instance , callable_type : CallableType ) -> Instance :
3640
3736
"""Creates a fake type that represents the intersection of an Instance and a CallableType.
3641
3737
@@ -3650,20 +3746,9 @@ def intersect_instance_callable(self, typ: Instance, callable_type: CallableType
3650
3746
gen_name = gen_unique_name ("<callable subtype of {}>" .format (typ .type .name ),
3651
3747
cur_module .names )
3652
3748
3653
- # Build the fake ClassDef and TypeInfo together.
3654
- # The ClassDef is full of lies and doesn't actually contain a body.
3655
- # Use format_bare to generate a nice name for error messages.
3656
- # We skip fully filling out a handful of TypeInfo fields because they
3657
- # should be irrelevant for a generated type like this:
3658
- # is_protocol, protocol_members, is_abstract
3749
+ # Synthesize a fake TypeInfo
3659
3750
short_name = format_type_bare (typ )
3660
- cdef = ClassDef (short_name , Block ([]))
3661
- cdef .fullname = cur_module .fullname + '.' + gen_name
3662
- info = TypeInfo (SymbolTable (), cdef , cur_module .fullname )
3663
- cdef .info = info
3664
- info .bases = [typ ]
3665
- calculate_mro (info )
3666
- info .calculate_metaclass_type ()
3751
+ cdef , info = self .make_fake_typeinfo (cur_module .fullname , gen_name , short_name , [typ ])
3667
3752
3668
3753
# Build up a fake FuncDef so we can populate the symbol table.
3669
3754
func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
@@ -3828,9 +3913,11 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM
3828
3913
return {}, {}
3829
3914
expr = node .args [0 ]
3830
3915
if literal (expr ) == LITERAL_TYPE :
3831
- vartype = type_map [expr ]
3832
- type = get_isinstance_type (node .args [1 ], type_map )
3833
- return conditional_type_map (expr , vartype , type )
3916
+ return self .conditional_type_map_with_intersection (
3917
+ expr ,
3918
+ type_map [expr ],
3919
+ get_isinstance_type (node .args [1 ], type_map ),
3920
+ )
3834
3921
elif refers_to_fullname (node .callee , 'builtins.issubclass' ):
3835
3922
if len (node .args ) != 2 : # the error will be reported elsewhere
3836
3923
return {}, {}
@@ -4309,6 +4396,10 @@ def refine_identity_comparison_expression(self,
4309
4396
4310
4397
if enum_name is not None :
4311
4398
expr_type = try_expanding_enum_to_union (expr_type , enum_name )
4399
+
4400
+ # We intentionally use 'conditional_type_map' directly here instead of
4401
+ # 'self.conditional_type_map_with_intersection': we only compute ad-hoc
4402
+ # intersections when working with pure instances.
4312
4403
partial_type_maps .append (conditional_type_map (expr , expr_type , target_type ))
4313
4404
4314
4405
return reduce_conditional_maps (partial_type_maps )
@@ -4726,10 +4817,55 @@ def infer_issubclass_maps(self, node: CallExpr,
4726
4817
# Any other object whose type we don't know precisely
4727
4818
# for example, Any or a custom metaclass.
4728
4819
return {}, {} # unknown type
4729
- yes_map , no_map = conditional_type_map (expr , vartype , type )
4820
+ yes_map , no_map = self . conditional_type_map_with_intersection (expr , vartype , type )
4730
4821
yes_map , no_map = map (convert_to_typetype , (yes_map , no_map ))
4731
4822
return yes_map , no_map
4732
4823
4824
+ def conditional_type_map_with_intersection (self ,
4825
+ expr : Expression ,
4826
+ expr_type : Type ,
4827
+ type_ranges : Optional [List [TypeRange ]],
4828
+ ) -> Tuple [TypeMap , TypeMap ]:
4829
+ # For some reason, doing "yes_map, no_map = conditional_type_maps(...)"
4830
+ # doesn't work: mypyc will decide that 'yes_map' is of type None if we try.
4831
+ initial_maps = conditional_type_map (expr , expr_type , type_ranges )
4832
+ yes_map = initial_maps [0 ] # type: TypeMap
4833
+ no_map = initial_maps [1 ] # type: TypeMap
4834
+
4835
+ if yes_map is not None or type_ranges is None :
4836
+ return yes_map , no_map
4837
+
4838
+ # If conditions_type_map was unable to successfully narrow the expr_type
4839
+ # using the type_ranges and concluded if-branch is unreachable, we try
4840
+ # computing it again using a different algorithm that tries to generate
4841
+ # an ad-hoc intersection between the expr_type and the type_ranges.
4842
+ expr_type = get_proper_type (expr_type )
4843
+ if isinstance (expr_type , UnionType ):
4844
+ possible_expr_types = get_proper_types (expr_type .relevant_items ())
4845
+ else :
4846
+ possible_expr_types = [expr_type ]
4847
+
4848
+ possible_target_types = []
4849
+ for tr in type_ranges :
4850
+ item = get_proper_type (tr .item )
4851
+ if not isinstance (item , Instance ) or tr .is_upper_bound :
4852
+ return yes_map , no_map
4853
+ possible_target_types .append (item )
4854
+
4855
+ out = []
4856
+ for v in possible_expr_types :
4857
+ if not isinstance (v , Instance ):
4858
+ return yes_map , no_map
4859
+ for t in possible_target_types :
4860
+ intersection = self .intersect_instances ([v , t ], expr )
4861
+ if intersection is None :
4862
+ continue
4863
+ out .append (intersection )
4864
+ if len (out ) == 0 :
4865
+ return None , {}
4866
+ new_yes_type = make_simplified_union (out )
4867
+ return {expr : new_yes_type }, {}
4868
+
4733
4869
4734
4870
def conditional_type_map (expr : Expression ,
4735
4871
current_type : Optional [Type ],
0 commit comments