Skip to content

Commit 4081fd9

Browse files
authored
Merge branch 'master' into bugfix/st-override-with-property-typevar
2 parents e77b8a5 + 1affabe commit 4081fd9

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

mypy/checker.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,19 +2740,20 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
27402740
return
27412741
# Verify that inherited attributes are compatible.
27422742
mro = typ.mro[1:]
2743-
for i, base in enumerate(mro):
2743+
all_names = {name for base in mro for name in base.names}
2744+
for name in sorted(all_names - typ.names.keys()):
2745+
# Sort for reproducible message order.
27442746
# Attributes defined in both the type and base are skipped.
27452747
# Normal checks for attribute compatibility should catch any problems elsewhere.
2746-
non_overridden_attrs = base.names.keys() - typ.names.keys()
2747-
for name in non_overridden_attrs:
2748-
if is_private(name):
2749-
continue
2750-
for base2 in mro[i + 1 :]:
2751-
# We only need to check compatibility of attributes from classes not
2752-
# in a subclass relationship. For subclasses, normal (single inheritance)
2753-
# checks suffice (these are implemented elsewhere).
2754-
if name in base2.names and base2 not in base.mro:
2755-
self.check_compatibility(name, base, base2, typ)
2748+
if is_private(name):
2749+
continue
2750+
# Compare the first base defining a name with the rest.
2751+
# Remaining bases may not be pairwise compatible as the first base provides
2752+
# the used definition.
2753+
i, base = next((i, base) for i, base in enumerate(mro) if name in base.names)
2754+
for base2 in mro[i + 1 :]:
2755+
if name in base2.names and base2 not in base.mro:
2756+
self.check_compatibility(name, base, base2, typ)
27562757

27572758
def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None:
27582759
if sym.type is not None:
@@ -2833,8 +2834,10 @@ class C(B, A[int]): ... # this is unsafe because...
28332834
ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True)
28342835
elif first_type and second_type:
28352836
if isinstance(first.node, Var):
2837+
first_type = get_proper_type(map_type_from_supertype(first_type, ctx, base1))
28362838
first_type = expand_self_type(first.node, first_type, fill_typevars(ctx))
28372839
if isinstance(second.node, Var):
2840+
second_type = get_proper_type(map_type_from_supertype(second_type, ctx, base2))
28382841
second_type = expand_self_type(second.node, second_type, fill_typevars(ctx))
28392842
ok = is_equivalent(first_type, second_type)
28402843
if not ok:

mypyc/test/test_run.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from mypyc.codegen import emitmodule
2525
from mypyc.errors import Errors
2626
from mypyc.options import CompilerOptions
27+
from mypyc.test.config import test_data_prefix
2728
from mypyc.test.test_serialization import check_serialization_roundtrip
2829
from mypyc.test.testutil import (
2930
ICODE_GEN_BUILTINS,
@@ -291,9 +292,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
291292
# No driver.py provided by test case. Use the default one
292293
# (mypyc/test-data/driver/driver.py) that calls each
293294
# function named test_*.
294-
default_driver = os.path.join(
295-
os.path.dirname(__file__), "..", "test-data", "driver", "driver.py"
296-
)
295+
default_driver = os.path.join(test_data_prefix, "driver", "driver.py")
297296
shutil.copy(default_driver, driver_path)
298297
env = os.environ.copy()
299298
env["MYPYC_RUN_BENCH"] = "1" if bench else "0"

test-data/unit/check-generic-subtyping.test

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,3 +1185,38 @@ class GoodPropertyOverride(A["GoodPropertyOverride"]):
11851185
@member.setter
11861186
def member(self, val: Self): ...
11871187
[builtins fixtures/property.pyi]
1188+
1189+
[case testMultipleInheritanceCompatibleTypeVar]
1190+
from typing import Generic, TypeVar
1191+
1192+
T = TypeVar("T")
1193+
U = TypeVar("U")
1194+
1195+
class A(Generic[T]):
1196+
x: T
1197+
def fn(self, t: T) -> None: ...
1198+
1199+
class A2(A[T]):
1200+
y: str
1201+
z: str
1202+
1203+
class B(Generic[T]):
1204+
x: T
1205+
def fn(self, t: T) -> None: ...
1206+
1207+
class C1(A2[str], B[str]): pass
1208+
class C2(A2[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \
1209+
# E: Definition of "x" in base class "A" is incompatible with definition in base class "B"
1210+
class C3(A2[T], B[T]): pass
1211+
class C4(A2[U], B[U]): pass
1212+
class C5(A2[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \
1213+
# E: Definition of "x" in base class "A" is incompatible with definition in base class "B"
1214+
1215+
class D1(A[str], B[str]): pass
1216+
class D2(A[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \
1217+
# E: Definition of "x" in base class "A" is incompatible with definition in base class "B"
1218+
class D3(A[T], B[T]): pass
1219+
class D4(A[U], B[U]): pass
1220+
class D5(A[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \
1221+
# E: Definition of "x" in base class "A" is incompatible with definition in base class "B"
1222+
[builtins fixtures/tuple.pyi]

test-data/unit/check-multiple-inheritance.test

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,29 @@ class C34(B3, B4): ...
706706
class C41(B4, B1): ...
707707
class C42(B4, B2): ...
708708
class C43(B4, B3): ...
709+
710+
[case testMultipleInheritanceExplicitDiamondResolution]
711+
# Adapted from #14279
712+
class A:
713+
class M:
714+
pass
715+
716+
class B0(A):
717+
class M(A.M):
718+
pass
719+
720+
class B1(A):
721+
class M(A.M):
722+
pass
723+
724+
class C(B0,B1):
725+
class M(B0.M, B1.M):
726+
pass
727+
728+
class D0(B0):
729+
pass
730+
class D1(B1):
731+
pass
732+
733+
class D(D0,D1,C):
734+
pass

0 commit comments

Comments
 (0)