Skip to content

[mypyc] feat: cache len for iterating over immutable types and expressions with length known at compile time #19503

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

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e81a5ce
feat: cache len for iterating over immutable types
BobTheBuidler Aug 4, 2025
804c3b5
fix: tests
BobTheBuidler Aug 4, 2025
33d64b3
fix: include bytes as sequence and fix test
BobTheBuidler Aug 4, 2025
34c2244
rtypes
BobTheBuidler Aug 4, 2025
58b18ee
Update for_helpers.py
BobTheBuidler Aug 4, 2025
aef2732
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 4, 2025
3bd549c
Update for_helpers.py
BobTheBuidler Aug 4, 2025
48a3671
Update specialize.py
BobTheBuidler Aug 4, 2025
3f7ba01
Update irbuild-tuple.test
BobTheBuidler Aug 4, 2025
74b89d8
Delete mypyc/test-data/fixtures/tuple.pyi
BobTheBuidler Aug 4, 2025
05b6f53
feat: len only once
BobTheBuidler Aug 4, 2025
4c95554
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 4, 2025
83ee9aa
Update for_helpers.py
BobTheBuidler Aug 4, 2025
d8dd01a
Update for_helpers.py
BobTheBuidler Aug 4, 2025
b16e023
Update for_helpers.py
BobTheBuidler Aug 4, 2025
8f5f59e
Update for_helpers.py
BobTheBuidler Aug 4, 2025
a0f2ac0
Update for_helpers.py
BobTheBuidler Aug 4, 2025
47f1d3f
Update for_helpers.py
BobTheBuidler Aug 4, 2025
bf7520a
Update irbuild-tuple.test
BobTheBuidler Aug 4, 2025
558b1af
feat: handle star expr
BobTheBuidler Aug 4, 2025
24c67d3
feat(test): test final and list
BobTheBuidler Aug 4, 2025
f09ef04
feat: propagate length of final strings
BobTheBuidler Aug 4, 2025
0a0508b
feat(test): test containers from literal exprs
BobTheBuidler Aug 4, 2025
ed4239b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 4, 2025
7eb67a3
Update for_helpers.py
BobTheBuidler Aug 4, 2025
e9f0451
Update for_helpers.py
BobTheBuidler Aug 4, 2025
537c8af
feat(test): test stars
BobTheBuidler Aug 4, 2025
888b971
Update for_helpers.py
BobTheBuidler Aug 4, 2025
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
76 changes: 44 additions & 32 deletions mypyc/ir/rtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def may_be_immortal(self) -> bool:
def serialize(self) -> str:
return "void"

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RVoid]:
return isinstance(other, RVoid)

def __hash__(self) -> int:
Expand Down Expand Up @@ -279,7 +279,7 @@ def serialize(self) -> str:
def __repr__(self) -> str:
return "<RPrimitive %s>" % self.name

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RPrimitive]:
return isinstance(other, RPrimitive) and other.name == self.name

def __hash__(self) -> int:
Expand Down Expand Up @@ -513,15 +513,15 @@ def __hash__(self) -> int:
range_rprimitive: Final = RPrimitive("builtins.range", is_unboxed=False, is_refcounted=True)


def is_tagged(rtype: RType) -> bool:
def is_tagged(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is int_rprimitive or rtype is short_int_rprimitive


def is_int_rprimitive(rtype: RType) -> bool:
def is_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is int_rprimitive


def is_short_int_rprimitive(rtype: RType) -> bool:
def is_short_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is short_int_rprimitive


Expand All @@ -535,7 +535,7 @@ def is_int32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
)


def is_int64_rprimitive(rtype: RType) -> bool:
def is_int64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is int64_rprimitive or (
rtype is c_pyssize_t_rprimitive and rtype._ctype == "int64_t"
)
Expand All @@ -554,81 +554,93 @@ def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is uint8_rprimitive


def is_uint32_rprimitive(rtype: RType) -> bool:
def is_uint32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is uint32_rprimitive


def is_uint64_rprimitive(rtype: RType) -> bool:
def is_uint64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is uint64_rprimitive


def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool:
def is_c_py_ssize_t_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is c_pyssize_t_rprimitive


def is_pointer_rprimitive(rtype: RType) -> bool:
def is_pointer_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return rtype is pointer_rprimitive


def is_float_rprimitive(rtype: RType) -> bool:
def is_float_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.float"


def is_bool_rprimitive(rtype: RType) -> bool:
def is_bool_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bool"


def is_bit_rprimitive(rtype: RType) -> bool:
def is_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "bit"


def is_bool_or_bit_rprimitive(rtype: RType) -> bool:
def is_bool_or_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype)


def is_object_rprimitive(rtype: RType) -> bool:
def is_object_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.object"


def is_none_rprimitive(rtype: RType) -> bool:
def is_none_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.None"


def is_list_rprimitive(rtype: RType) -> bool:
def is_list_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.list"


def is_dict_rprimitive(rtype: RType) -> bool:
def is_dict_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.dict"


def is_set_rprimitive(rtype: RType) -> bool:
def is_set_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.set"


def is_frozenset_rprimitive(rtype: RType) -> bool:
def is_frozenset_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.frozenset"


def is_str_rprimitive(rtype: RType) -> bool:
def is_str_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.str"


def is_bytes_rprimitive(rtype: RType) -> bool:
def is_bytes_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bytes"


def is_tuple_rprimitive(rtype: RType) -> bool:
def is_tuple_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.tuple"


def is_range_rprimitive(rtype: RType) -> bool:
def is_range_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and rtype.name == "builtins.range"


def is_sequence_rprimitive(rtype: RType) -> bool:
def is_sequence_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return isinstance(rtype, RPrimitive) and (
is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype)
is_list_rprimitive(rtype)
or is_tuple_rprimitive(rtype)
or is_str_rprimitive(rtype)
or is_bytes_rprimitive(rtype)
)


def is_immutable_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
return (
is_str_rprimitive(rtype)
or is_bytes_rprimitive(rtype)
or is_tuple_rprimitive(rtype)
or is_frozenset_rprimitive(rtype)
)


Expand Down Expand Up @@ -717,7 +729,7 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return "<RTuple %s>" % ", ".join(repr(typ) for typ in self.types)

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RTuple]:
return isinstance(other, RTuple) and self.types == other.types

def __hash__(self) -> int:
Expand Down Expand Up @@ -850,7 +862,7 @@ def __repr__(self) -> str:
", ".join(name + ":" + repr(typ) for name, typ in zip(self.names, self.types)),
)

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RStruct]:
return (
isinstance(other, RStruct)
and self.name == other.name
Expand Down Expand Up @@ -920,7 +932,7 @@ def attr_type(self, name: str) -> RType:
def __repr__(self) -> str:
return "<RInstance %s>" % self.name

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RInstance]:
return isinstance(other, RInstance) and other.name == self.name

def __hash__(self) -> int:
Expand Down Expand Up @@ -974,7 +986,7 @@ def __str__(self) -> str:
return "union[%s]" % ", ".join(str(item) for item in self.items)

# We compare based on the set because order in a union doesn't matter
def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RUnion]:
return isinstance(other, RUnion) and self.items_set == other.items_set

def __hash__(self) -> int:
Expand Down Expand Up @@ -1016,7 +1028,7 @@ def optional_value_type(rtype: RType) -> RType | None:
return None


def is_optional_type(rtype: RType) -> bool:
def is_optional_type(rtype: RType) -> TypeGuard[RUnion]:
"""Is rtype an optional type with exactly two union items?"""
return optional_value_type(rtype) is not None

Expand Down Expand Up @@ -1048,7 +1060,7 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return f"<RArray {self.item_type!r}[{self.length}]>"

def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> TypeGuard[RArray]:
return (
isinstance(other, RArray)
and self.item_type == other.item_type
Expand Down
7 changes: 6 additions & 1 deletion mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
RType,
RUnion,
bitmap_rprimitive,
bytes_rprimitive,
c_pyssize_t_rprimitive,
dict_rprimitive,
int_rprimitive,
Expand Down Expand Up @@ -958,8 +959,12 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType:
elif isinstance(target_type, Instance):
if target_type.type.fullname == "builtins.str":
return str_rprimitive
else:
elif target_type.type.fullname == "builtins.bytes":
return bytes_rprimitive
try:
return self.type_to_rtype(target_type.args[0])
except IndexError:
raise ValueError(f"{target_type!r} is not a valid sequence.") from None
# This elif-blocks are needed for iterating over classes derived from NamedTuple.
elif isinstance(target_type, TypeVarLikeType):
return self.get_sequence_type_from_type(target_type.upper_bound)
Expand Down
Loading
Loading