Skip to content

Commit acf26f4

Browse files
authored
[mypyc] Optimize int()/float()/complex() on native classes (#14450)
int() and float() calls on native classes can simply call the associated dunder if the RInstance defines it, no need to load the type and call it. bool() calls were already optimized merely a few days ago, but there wasn't an IRbuild test verifying this so I added one. --- Follow up to #14422. I saw the PR and it reminded me that I had this old patch laying around :)
1 parent f625602 commit acf26f4

File tree

3 files changed

+41
-15
lines changed

3 files changed

+41
-15
lines changed

mypy/nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,7 @@ class NameExpr(RefExpr):
17771777

17781778
def __init__(self, name: str) -> None:
17791779
super().__init__()
1780-
self.name = name # Name referred to (may be qualified)
1780+
self.name = name # Name referred to
17811781
# Is this a l.h.s. of a special form assignment like typed dict or type variable?
17821782
self.is_special_form = False
17831783

mypyc/irbuild/specialize.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,20 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va
157157

158158

159159
@specialize_function("builtins.abs")
160-
def translate_abs(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
161-
"""Specialize calls on native classes that implement __abs__."""
162-
if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]:
160+
@specialize_function("builtins.int")
161+
@specialize_function("builtins.float")
162+
@specialize_function("builtins.complex")
163+
def translate_builtins_with_unary_dunder(
164+
builder: IRBuilder, expr: CallExpr, callee: RefExpr
165+
) -> Value | None:
166+
"""Specialize calls on native classes that implement the associated dunder."""
167+
if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS] and isinstance(callee, NameExpr):
163168
arg = expr.args[0]
164169
arg_typ = builder.node_type(arg)
165-
if isinstance(arg_typ, RInstance) and arg_typ.class_ir.has_method("__abs__"):
170+
method = f"__{callee.name}__"
171+
if isinstance(arg_typ, RInstance) and arg_typ.class_ir.has_method(method):
166172
obj = builder.accept(arg)
167-
return builder.gen_method_call(obj, "__abs__", [], None, expr.line)
173+
return builder.gen_method_call(obj, method, [], None, expr.line)
168174

169175
return None
170176

mypyc/test-data/irbuild-dunders.test

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,21 @@ class C:
154154
def __abs__(self) -> int:
155155
return 6
156156

157+
def __bool__(self) -> bool:
158+
return False
159+
160+
def __complex__(self) -> complex:
161+
return 7j
162+
157163
def f(c: C) -> None:
158164
-c
159165
~c
160166
int(c)
161167
float(c)
162168
+c
163169
abs(c)
170+
bool(c)
171+
complex(c)
164172
[out]
165173
def C.__neg__(self):
166174
self :: __main__.C
@@ -188,19 +196,31 @@ def C.__abs__(self):
188196
self :: __main__.C
189197
L0:
190198
return 12
199+
def C.__bool__(self):
200+
self :: __main__.C
201+
L0:
202+
return 0
203+
def C.__complex__(self):
204+
self :: __main__.C
205+
r0 :: object
206+
L0:
207+
r0 = 7j
208+
return r0
191209
def f(c):
192210
c :: __main__.C
193-
r0, r1 :: int
194-
r2, r3, r4, r5 :: object
195-
r6, r7 :: int
211+
r0, r1, r2 :: int
212+
r3 :: float
213+
r4, r5 :: int
214+
r6 :: bool
215+
r7 :: object
196216
L0:
197217
r0 = c.__neg__()
198218
r1 = c.__invert__()
199-
r2 = load_address PyLong_Type
200-
r3 = PyObject_CallFunctionObjArgs(r2, c, 0)
201-
r4 = load_address PyFloat_Type
202-
r5 = PyObject_CallFunctionObjArgs(r4, c, 0)
203-
r6 = c.__pos__()
204-
r7 = c.__abs__()
219+
r2 = c.__int__()
220+
r3 = c.__float__()
221+
r4 = c.__pos__()
222+
r5 = c.__abs__()
223+
r6 = c.__bool__()
224+
r7 = c.__complex__()
205225
return 1
206226

0 commit comments

Comments
 (0)