@@ -2744,23 +2744,44 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
27442744 if len (typ .bases ) <= 1 :
27452745 # No multiple inheritance.
27462746 return
2747+
27472748 # Verify that inherited attributes are compatible.
2748- bases = typ .bases
2749- all_names = [{n for p in b .type .mro for n in p .names } for b in bases ]
2750- for i , base in enumerate (bases ):
2749+ # Construct a "typed" MRO that follows regular MRO order, but includes instances
2750+ # parametrized with their generic args.
2751+ # This detects e.g. `class A(Mapping[int, str], Iterable[str])` correctly.
2752+ # For each MRO entry, include it parametrized according to each base inheriting
2753+ # from it.
2754+ typed_mro = [
2755+ map_instance_to_supertype (base , parent )
2756+ for parent in typ .mro [1 :]
2757+ for base in typ .bases
2758+ if parent in base .type .mro
2759+ ]
2760+ # If the first MRO entry is compatible with everything following, we don't need
2761+ # (and shouldn't) compare further pairs
2762+ # (see testMultipleInheritanceExplcitDiamondResolution)
2763+ seen_names = set ()
2764+ for i , base in enumerate (typed_mro ):
27512765 # Attributes defined in both the type and base are skipped.
27522766 # Normal checks for attribute compatibility should catch any problems elsewhere.
27532767 # Sort for consistent messages order.
2754- non_overridden_attrs = sorted (all_names [i ] - typ .names .keys ())
2768+ non_overridden_attrs = sorted (typed_mro [i ]. type . names - typ .names .keys ())
27552769 for name in non_overridden_attrs :
27562770 if is_private (name ):
27572771 continue
2758- for j , base2 in enumerate (bases [i + 1 :], i + 1 ):
2772+ if name in seen_names :
2773+ continue
2774+ for j , base2 in enumerate (typed_mro [i + 1 :], i + 1 ):
27592775 # We only need to check compatibility of attributes from classes not
27602776 # in a subclass relationship. For subclasses, normal (single inheritance)
27612777 # checks suffice (these are implemented elsewhere).
2762- if name in all_names [j ] and base .type != base2 .type :
2778+ if name in base2 .type .names and not is_subtype (
2779+ base , base2 , ignore_promotions = True
2780+ ):
2781+ # If base1 already inherits from base2 with correct type args,
2782+ # we have reported errors if any. Avoid reporting them again.
27632783 self .check_compatibility (name , base , base2 , typ )
2784+ seen_names .add (name )
27642785
27652786 def determine_type_of_member (self , node : SymbolNode ) -> Type | None :
27662787 if isinstance (node , FuncBase ):
@@ -2810,48 +2831,17 @@ class C(B, A[int]): ... # this is unsafe because...
28102831 # __init__ and friends can be incompatible -- it's a special case.
28112832 return
28122833
2813- if is_subtype (base1 , base2 , ignore_promotions = True ):
2814- # If base1 already inherits from base2 with correct type args,
2815- # we have reported errors if any. Avoid reporting them again.
2816- return
2817-
28182834 first_type = first_node = None
28192835 second_type = second_node = None
28202836 orig_var = ctx .get (name )
28212837
28222838 if orig_var is not None and orig_var .node is not None :
2823- if (b1type := base1 .type .get_containing_type_info (name )) is not None :
2824- base1 = map_instance_to_supertype (base1 , b1type )
2825- first_type , first_node = self .attribute_type_from_base (
2826- orig_var .node , base1 .type , base1
2827- )
2828-
2829- if (b2type := base2 .type .get_containing_type_info (name )) is not None :
2830- base2 = map_instance_to_supertype (base2 , b2type )
2831- second_type , second_node = self .attribute_type_from_base (
2832- orig_var .node , base2 .type , base2
2833- )
2834-
2835- # Fix the order. We iterate over the explicit bases, which means we may
2836- # end up with the following structure:
2837- # class A:
2838- # def fn(self, x: int) -> None: ...
2839- # class B(A): ...
2840- # class C(A):
2841- # def fn(self, x: int|str) -> None: ...
2842- # class D(B, C): ...
2843- # Here D.fn will actually be dispatched to C.fn which is assignable to A.fn,
2844- # but without this fixup we'd check A.fn against C.fn instead.
2845- # See testMultipleInheritanceTransitive in check-multiple-inheritance.test
2846- if (
2847- b1type is not None
2848- and b2type is not None
2849- and ctx .mro .index (b1type ) > ctx .mro .index (b2type )
2850- ):
2851- b1type , b2type = b2type , b1type
2852- base1 , base2 = base2 , base1
2853- first_type , second_type = second_type , first_type
2854- first_node , second_node = second_node , first_node
2839+ first_type , first_node = self .attribute_type_from_base (
2840+ orig_var .node , base1 .type , base1
2841+ )
2842+ second_type , second_node = self .attribute_type_from_base (
2843+ orig_var .node , base2 .type , base2
2844+ )
28552845
28562846 # TODO: use more principled logic to decide is_subtype() vs is_equivalent().
28572847 # We should rely on mutability of superclass node, not on types being Callable.
0 commit comments