Skip to content

[mypyc] Add primitives for isinstance of built-in types #19435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions mypyc/irbuild/specialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,26 @@
join_formatted_strings,
tokenizer_format_call,
)
from mypyc.primitives.bytes_ops import isinstance_bytearray, isinstance_bytes
from mypyc.primitives.dict_ops import (
dict_items_op,
dict_keys_op,
dict_setdefault_spec_init_op,
dict_values_op,
isinstance_dict,
)
from mypyc.primitives.float_ops import isinstance_float
from mypyc.primitives.int_ops import isinstance_int
from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op
from mypyc.primitives.misc_ops import isinstance_bool
from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set
from mypyc.primitives.str_ops import (
isinstance_str,
str_encode_ascii_strict,
str_encode_latin1_strict,
str_encode_utf8_strict,
)
from mypyc.primitives.tuple_ops import new_tuple_set_item_op
from mypyc.primitives.tuple_ops import isinstance_tuple, new_tuple_set_item_op

# Specializers are attempted before compiling the arguments to the
# function. Specializers can return None to indicate that they failed
Expand Down Expand Up @@ -546,7 +553,19 @@ def gen_inner_stmts() -> None:
return retval


isinstance_primitives: Final = {"builtins.list": isinstance_list}
isinstance_primitives: Final = {
"builtins.bool": isinstance_bool,
"builtins.bytearray": isinstance_bytearray,
"builtins.bytes": isinstance_bytes,
"builtins.dict": isinstance_dict,
"builtins.float": isinstance_float,
"builtins.frozenset": isinstance_frozenset,
"builtins.int": isinstance_int,
"builtins.list": isinstance_list,
"builtins.set": isinstance_set,
"builtins.str": isinstance_str,
"builtins.tuple": isinstance_tuple,
}


@specialize_function("builtins.isinstance")
Expand Down
21 changes: 20 additions & 1 deletion mypyc/primitives/bytes_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

from __future__ import annotations

from mypyc.ir.ops import ERR_MAGIC
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
RUnion,
bit_rprimitive,
bytes_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
Expand Down Expand Up @@ -35,6 +36,15 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, bytes)
isinstance_bytes = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyBytes_Check",
error_kind=ERR_NEVER,
)

# bytearray(obj)
function_op(
name="builtins.bytearray",
Expand All @@ -44,6 +54,15 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, bytearray)
isinstance_bytearray = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyByteArray_Check",
error_kind=ERR_NEVER,
)

# bytes ==/!= (return -1/0/1)
bytes_compare = custom_op(
arg_types=[bytes_rprimitive, bytes_rprimitive],
Expand Down
9 changes: 9 additions & 0 deletions mypyc/primitives/dict_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, dict)
isinstance_dict = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyDict_Check",
error_kind=ERR_NEVER,
)

# dict[key]
dict_get_item_op = method_op(
name="__getitem__",
Expand Down
10 changes: 10 additions & 0 deletions mypyc/primitives/float_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
bool_rprimitive,
float_rprimitive,
int_rprimitive,
Expand Down Expand Up @@ -166,3 +167,12 @@
c_function_name="CPyFloat_IsNaN",
error_kind=ERR_NEVER,
)

# translate isinstance(obj, float)
isinstance_float = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyFloat_Check",
error_kind=ERR_NEVER,
)
9 changes: 9 additions & 0 deletions mypyc/primitives/int_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,12 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription:
c_function_name="CPyUInt8_Overflow",
error_kind=ERR_ALWAYS,
)

# translate isinstance(obj, int)
isinstance_int = function_op(
name="builtints.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyLong_Check",
error_kind=ERR_NEVER,
)
2 changes: 1 addition & 1 deletion mypyc/primitives/list_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
extra_int_constants=[(0, int_rprimitive)],
)

# isinstance(obj, list)
# translate isinstance(obj, list)
isinstance_list = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
Expand Down
9 changes: 9 additions & 0 deletions mypyc/primitives/misc_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@
truncated_type=bool_rprimitive,
)

# isinstance(obj, bool)
isinstance_bool = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyBool_Check",
error_kind=ERR_NEVER,
)

# slice(start, stop, step)
new_slice_op = function_op(
name="builtins.slice",
Expand Down
20 changes: 19 additions & 1 deletion mypyc/primitives/set_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC
from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
bool_rprimitive,
Expand Down Expand Up @@ -64,6 +64,24 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, set)
isinstance_set = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PySet_Check",
error_kind=ERR_NEVER,
)

# translate isinstance(obj, frozenset)
isinstance_frozenset = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyFrozenSet_Check",
error_kind=ERR_NEVER,
)

# item in set
set_in_op = binary_op(
name="in",
Expand Down
9 changes: 9 additions & 0 deletions mypyc/primitives/str_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, str)
isinstance_str = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyUnicode_Check",
error_kind=ERR_NEVER,
)

# str1 + str2
binary_op(
name="+",
Expand Down
10 changes: 10 additions & 0 deletions mypyc/primitives/tuple_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
c_pyssize_t_rprimitive,
int_rprimitive,
list_rprimitive,
Expand Down Expand Up @@ -83,6 +84,15 @@
error_kind=ERR_MAGIC,
)

# translate isinstance(obj, tuple)
isinstance_tuple = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyTuple_Check",
error_kind=ERR_NEVER,
)

# tuple + tuple
binary_op(
name="+",
Expand Down
37 changes: 13 additions & 24 deletions mypyc/test-data/irbuild-basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -1581,24 +1581,18 @@ def main() -> None:
[out]
def foo(x):
x :: union[int, str]
r0 :: object
r1 :: i32
r2 :: bit
r3 :: bool
r4 :: __main__.B
r5 :: __main__.A
r0 :: bit
r1 :: __main__.B
r2 :: __main__.A
L0:
r0 = load_address PyLong_Type
r1 = PyObject_IsInstance(x, r0)
r2 = r1 >= 0 :: signed
r3 = truncate r1: i32 to builtins.bool
if r3 goto L1 else goto L2 :: bool
r0 = PyLong_Check(x)
if r0 goto L1 else goto L2 :: bool
L1:
r4 = B()
return r4
r1 = B()
return r1
L2:
r5 = A()
return r5
r2 = A()
return r2
def main():
r0 :: object
r1 :: __main__.A
Expand Down Expand Up @@ -3389,16 +3383,11 @@ def f(x: object) -> bool:
return isinstance(x, bool)
[out]
def f(x):
x, r0 :: object
r1 :: i32
r2 :: bit
r3 :: bool
x :: object
r0 :: bit
L0:
r0 = load_address PyBool_Type
r1 = PyObject_IsInstance(x, r0)
r2 = r1 >= 0 :: signed
r3 = truncate r1: i32 to builtins.bool
return r3
r0 = PyBool_Check(x)
return r0

[case testRangeObject]
def range_object() -> None:
Expand Down
28 changes: 11 additions & 17 deletions mypyc/test-data/irbuild-i64.test
Original file line number Diff line number Diff line change
Expand Up @@ -2046,27 +2046,21 @@ L2:
return r6
def narrow2(x):
x :: union[__main__.C, i64]
r0 :: object
r1 :: i32
r2 :: bit
r3 :: bool
r4 :: i64
r5 :: __main__.C
r6 :: i64
r0 :: bit
r1 :: i64
r2 :: __main__.C
r3 :: i64
L0:
r0 = load_address PyLong_Type
r1 = PyObject_IsInstance(x, r0)
r2 = r1 >= 0 :: signed
r3 = truncate r1: i32 to builtins.bool
if r3 goto L1 else goto L2 :: bool
r0 = PyLong_Check(x)
if r0 goto L1 else goto L2 :: bool
L1:
r4 = unbox(i64, x)
return r4
r1 = unbox(i64, x)
return r1
L2:
r5 = borrow cast(__main__.C, x)
r6 = r5.a
r2 = borrow cast(__main__.C, x)
r3 = r2.a
keep_alive x
return r6
return r3

[case testI64ConvertBetweenTuples_64bit]
from __future__ import annotations
Expand Down
Loading