Skip to content

Commit 33bc712

Browse files
authored
[mypyc] Fix vtable construction for deep trait inheritance (#20917)
Fixes mypyc/mypyc#1189 The core issue: `compute_vtable` iterates `cls.traits` (direct parents only) instead of the full trait MRO, so when an intermediate trait is empty, grandparent trait methods are never added to the vtable. The difference between `cls.traits` and `all_traits` for the issue's repro is the following: ```Python3 Class Child ----------- - cls.traits: • Middle --> {} - all_traits: • Child --> {'derived': <FuncIR Child.derived>} • Middle --> {} • Base --> {'value': <FuncIR Base.value>} ```
1 parent 359ad93 commit 33bc712

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

mypyc/irbuild/vtable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def compute_vtable(cls: ClassIR) -> None:
3737

3838
all_traits = [t for t in cls.mro if t.is_trait]
3939

40-
for t in [cls] + cls.traits:
40+
for t in [cls] + [t for t in all_traits if t is not cls]:
4141
for fn in itertools.chain(t.methods.values()):
4242
# TODO: don't generate a new entry when we overload without changing the type
4343
if fn == cls.get_method(fn.name, prefer_method=True):

mypyc/test-data/run-classes.test

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5709,3 +5709,68 @@ def test_derived_acyclic() -> None:
57095709
assert d.x == 3
57105710
assert d.y == "hi"
57115711
assert not gc.is_tracked(d)
5712+
5713+
[case testTraitVtableGrandparentMethod]
5714+
from typing import Any
5715+
from mypy_extensions import trait
5716+
5717+
@trait
5718+
class Base:
5719+
def value(self) -> Any:
5720+
return 42
5721+
5722+
@trait
5723+
class Middle(Base):
5724+
pass
5725+
5726+
@trait
5727+
class Child(Middle):
5728+
def derived(self) -> Any:
5729+
return self.value()
5730+
5731+
class Concrete(Child):
5732+
pass
5733+
5734+
class Sub(Concrete):
5735+
def value(self) -> Any:
5736+
return 99
5737+
5738+
[file driver.py]
5739+
from native import Concrete, Sub
5740+
c = Concrete()
5741+
assert c.value() == 42
5742+
assert c.derived() == 42
5743+
s = Sub()
5744+
assert s.value() == 99
5745+
assert s.derived() == 99
5746+
5747+
[case testTraitVtableGrandparentMethodAllowInterpretedSubclasses]
5748+
from typing import Any
5749+
from mypy_extensions import trait, mypyc_attr
5750+
5751+
@mypyc_attr(allow_interpreted_subclasses=True)
5752+
@trait
5753+
class Base:
5754+
def value(self) -> Any:
5755+
return 42
5756+
5757+
@mypyc_attr(allow_interpreted_subclasses=True)
5758+
@trait
5759+
class Middle(Base):
5760+
pass
5761+
5762+
@mypyc_attr(allow_interpreted_subclasses=True)
5763+
@trait
5764+
class Child(Middle):
5765+
def derived(self) -> Any:
5766+
return self.value()
5767+
5768+
@mypyc_attr(allow_interpreted_subclasses=True)
5769+
class Concrete(Child):
5770+
pass
5771+
5772+
[file driver.py]
5773+
from native import Concrete
5774+
c = Concrete()
5775+
assert c.value() == 42
5776+
assert c.derived() == 42

0 commit comments

Comments
 (0)