diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 37f2add4abbd..e6833e3c1a02 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -2444,7 +2444,15 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val if isinstance(typ, RInstance): # TODO: Support use_pyssize_t assert not use_pyssize_t - length = self.gen_method_call(val, "__len__", [], int_rprimitive, line) + + if typ.class_ir.has_method("__len__") and typ.class_ir.is_method_final("__len__"): + # Direct C call for final native __len__ methods + decl = typ.class_ir.method_decl("__len__") + length = self.call(decl, [val], [ARG_POS], [None], line) + else: + # Fallback: generic method call for non-native or ambiguous cases + length = self.gen_method_call(val, "__len__", [], int_rprimitive, line) + length = self.coerce(length, int_rprimitive, line) ok, fail = BasicBlock(), BasicBlock() cond = self.binary_op(length, Integer(0), ">=", line) diff --git a/mypyc/test-data/irbuild-dunders.test b/mypyc/test-data/irbuild-dunders.test index 1796a7e2160e..5e3b8085437f 100644 --- a/mypyc/test-data/irbuild-dunders.test +++ b/mypyc/test-data/irbuild-dunders.test @@ -1,24 +1,92 @@ # Test cases for (some) dunder methods [case testDundersLen] +from typing import final + class C: def __len__(self) -> int: return 2 - +@final +class D: + def __len__(self) -> int: + return 2 +class E: + @final + def __len__(self) -> int: + return 2 +class F(C): + """def __len__(self) -> int: + return 3""" def f(c: C) -> int: return len(c) +def g(d: D) -> int: + return len(d) +def h(e: E) -> int: + return len(e) +def i(f: F) -> int: + return len(f) [out] def C.__len__(self): self :: __main__.C L0: return 4 +def D.__len__(self): + self :: __main__.D +L0: + return 4 +def E.__len__(self): + self :: __main__.E +L0: + return 4 def f(c): c :: __main__.C r0 :: int r1 :: bit r2 :: bool L0: - r0 = c.__len__() + r0 = C.__len__(c) + r1 = int_ge r0, 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = raise ValueError('__len__() should return >= 0') + unreachable +L2: + return r0 +def g(d): + d :: __main__.D + r0 :: int + r1 :: bit + r2 :: bool +L0: + r0 = D.__len__(d) + r1 = int_ge r0, 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = raise ValueError('__len__() should return >= 0') + unreachable +L2: + return r0 +def h(e): + e :: __main__.E + r0 :: int + r1 :: bit + r2 :: bool +L0: + r0 = E.__len__(e) + r1 = int_ge r0, 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = raise ValueError('__len__() should return >= 0') + unreachable +L2: + return r0 +def i(f): + f :: __main__.F + r0 :: int + r1 :: bit + r2 :: bool +L0: + r0 = C.__len__(f) r1 = int_ge r0, 0 if r1 goto L2 else goto L1 :: bool L1: