diff --git a/mypyc/irbuild/vtable.py b/mypyc/irbuild/vtable.py index 0aa699626525..f3ca81194ede 100644 --- a/mypyc/irbuild/vtable.py +++ b/mypyc/irbuild/vtable.py @@ -71,7 +71,7 @@ def specialize_parent_vtable(cls: ClassIR, parent: ClassIR) -> VTableEntries: or orig_parent_method.name == "__init__" ): entry = VTableMethod(entry.cls, entry.name, child_method, entry.shadow_method) - else: + elif entry.name in entry.cls.method_decls or entry.name in entry.cls.property_types: entry = VTableMethod( entry.cls, entry.name, diff --git a/mypyc/test-data/irbuild-glue-methods.test b/mypyc/test-data/irbuild-glue-methods.test index bdbdb7e2afa6..91bc9d4db491 100644 --- a/mypyc/test-data/irbuild-glue-methods.test +++ b/mypyc/test-data/irbuild-glue-methods.test @@ -509,3 +509,60 @@ L0: r0 = '' __mypyc_self__._x = r0 return 1 + +[case testNoGlueForOverrideOfDecoratedMethod_64bit] +from typing import TypeVar + +T = TypeVar("T") + +def dec(f: T) -> T: + return f + +class Base: + @dec + def f(self) -> object: + return None + +class Sub(Base): + def f(self) -> int: + return 1 + +def call_base(b: Base) -> object: + return b.f() + +def call_sub(s: Sub) -> int: + return s.f() +[out] +def dec(f): + f :: object +L0: + return f +def Base.f(self): + self :: __main__.Base + r0 :: object +L0: + r0 = box(None, 1) + return r0 +def Sub.f(self): + self :: __main__.Sub +L0: + return 2 +def call_base(b): + b :: __main__.Base + r0 :: str + r1 :: object[1] + r2 :: object_ptr + r3 :: object +L0: + r0 = 'f' + r1 = [b] + r2 = load_address r1 + r3 = PyObject_VectorcallMethod(r0, r2, 9223372036854775809, 0) + keep_alive b + return r3 +def call_sub(s): + s :: __main__.Sub + r0 :: int +L0: + r0 = s.f() + return r0 diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index d8965568af3d..18b5b0963133 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -6390,6 +6390,236 @@ Sub = type("Sub", (Base,), {}) s = Sub() assert s.method() == "a1" +[case testNativeMethodOverridesDecoratedMethod] +from typing import Any, Callable, Iterator, TypeVar +from mypy_extensions import mypyc_attr, trait + +F = TypeVar("F", bound=Callable[..., Any]) + +def same(f: F) -> F: + return f + +def to_str(f: Callable[..., int]) -> Callable[..., str]: + def wrapper(*args: Any) -> str: + return "s" + str(f(*args)) + return wrapper + +def drop_arg(f: Callable[[Any, int], int]) -> Callable[[Any], int]: + def wrapper(self: Any) -> int: + return f(self, 10) + return wrapper + +class Base: + @same + def o(self) -> object: + return "base" + + @to_str + def s(self) -> int: + return 5 + + @drop_arg + def n(self, x: int) -> int: + return x + 1 + + @same + def gen(self) -> Iterator[int]: + yield 1 + + @same + async def co(self) -> int: + return 1 + + @classmethod + @to_str + def c(cls) -> int: + return 6 + + @staticmethod + @to_str + def st() -> int: + return 7 + +class Native(Base): + def o(self) -> int: + return 3 + + def s(self) -> str: + return "native" + + def n(self) -> int: + return 2 + + def gen(self) -> Iterator[int]: + yield 2 + + async def co(self) -> int: + return 2 + + @classmethod + def c(cls) -> str: + return "c" + + @staticmethod + def st() -> str: + return "st" + +class Leaf(Native): + def s(self) -> str: + return "leaf" + +class Inherits(Native): + pass + +class Decorated(Base): + @same + def s(self) -> str: + return "decorated" + +@trait +class Trait: + @to_str + def s(self) -> int: + return 8 + +class Impl(Trait): + def s(self) -> str: + return "impl" + +class Uses(Trait): + pass + +@mypyc_attr(allow_interpreted_subclasses=True) +class Open: + @to_str + def s(self) -> int: + return 9 + +@mypyc_attr(allow_interpreted_subclasses=True) +class OpenNative(Open): + def s(self) -> str: + return "open" + +def o_base(b: Base) -> object: + return b.o() + +def o_native(b: Native) -> int: + return b.o() + +def s_base(b: Base) -> str: + return b.s() + +def s_native(b: Native) -> str: + return b.s() + +def n_base(b: Base) -> int: + return b.n() + +def n_native(b: Native) -> int: + return b.n() + +def gen_base(b: Base) -> list[int]: + return list(b.gen()) + +def gen_native(b: Native) -> list[int]: + return list(b.gen()) + +async def co_base(b: Base) -> int: + return await b.co() + +async def co_native(b: Native) -> int: + return await b.co() + +def s_trait(t: Trait) -> str: + return t.s() + +def s_impl(i: Impl) -> str: + return i.s() + +def s_open(o: Open) -> str: + return o.s() + +def s_open_native(o: OpenNative) -> str: + return o.s() + +def test_native_method_overrides_decorated_method() -> None: + assert o_base(Base()) == "base" + assert o_base(Native()) == 3 + assert o_native(Native()) == 3 + assert o_native(Leaf()) == 3 + + assert s_base(Base()) == "s5" + assert s_base(Native()) == "native" + assert s_base(Leaf()) == "leaf" + assert s_base(Inherits()) == "native" + assert s_base(Decorated()) == "decorated" + assert s_native(Native()) == "native" + assert s_native(Leaf()) == "leaf" + assert s_native(Inherits()) == "native" + + assert n_base(Base()) == 11 + assert n_base(Native()) == 2 + assert n_native(Native()) == 2 + + assert gen_base(Base()) == [1] + assert gen_base(Native()) == [2] + assert gen_native(Native()) == [2] + + b: Base = Native() + assert Base.c() == "s6" + assert Native.c() == "c" + assert b.c() == "c" + assert Base.st() == "s7" + assert Native.st() == "st" + assert b.st() == "st" + + assert s_trait(Impl()) == "impl" + assert s_trait(Uses()) == "s8" + assert s_impl(Impl()) == "impl" + + assert s_open(Open()) == "s9" + assert s_open(OpenNative()) == "open" + assert s_open_native(OpenNative()) == "open" + +[typing fixtures/typing-full.pyi] +[file driver.py] +import asyncio +from native import ( + Base, Native, Leaf, Open, OpenNative, co_base, co_native, s_open, s_open_native, + test_native_method_overrides_decorated_method, +) + +test_native_method_overrides_decorated_method() + +assert Base().o() == "base" +assert Native().o() == 3 +assert Base().s() == "s5" +assert Native().s() == "native" +assert Leaf().s() == "leaf" +assert Base().n() == 11 +assert Native().n() == 2 + +assert asyncio.run(co_base(Base())) == 1 +assert asyncio.run(co_base(Native())) == 2 +assert asyncio.run(co_native(Native())) == 2 + +class Interp(OpenNative): + def s(self) -> str: + return "interp" + +class InterpInherits(OpenNative): + pass + +class InterpOpen(Open): + def s(self) -> str: + return "interp open" + +assert s_open(Interp()) == "interp" +assert s_open_native(Interp()) == "interp" +assert s_open(InterpInherits()) == "open" +assert s_open_native(InterpInherits()) == "open" +assert s_open(InterpOpen()) == "interp open" + [case testClassVarSelfReference] # ClassVar initializers that reference other ClassVars from the same class. # In CPython, the class body executes as a function where earlier assignments