From aaa9a6c22fb52cc26e5c15a8e558352a2e8cee0f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 8 Aug 2025 22:17:27 +0100 Subject: [PATCH 1/3] Cache common builtins instances --- mypy/checker.py | 25 +++++++++++++++++++++++++ mypy/semanal.py | 25 ++++++++++++++++++------- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6176df84c225..6ae648077962 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -431,6 +431,12 @@ def __init__( self._expr_checker = mypy.checkexpr.ExpressionChecker( self, self.msg, self.plugin, per_line_checking_time_ns ) + + self._object_type: Instance | None = None + self._type_type: Instance | None = None + self._str_type: Instance | None = None + self._function_type: Instance | None = None + self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options) self._unique_id = 0 @@ -7369,6 +7375,25 @@ def named_type(self, name: str) -> Instance: For example, named_type('builtins.object') produces the 'object' type. """ + if name == "builtins.function": + if self._function_type is None: + self._function_type = self._named_type(name) + return self._function_type + if name == "builtins.object": + if self._object_type is None: + self._object_type = self._named_type(name) + return self._object_type + if name == "builtins.type": + if self._type_type is None: + self._type_type = self._named_type(name) + return self._type_type + if name == "builtins.str": + if self._str_type is None: + self._str_type = self._named_type(name) + return self._str_type + return self._named_type(name) + + def _named_type(self, name: str) -> Instance: # Assume that the name refers to a type. sym = self.lookup_qualified(name) node = sym.node diff --git a/mypy/semanal.py b/mypy/semanal.py index fb66fb5158db..3e7a091e07c5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -497,6 +497,10 @@ def __init__( # Used to track edge case when return is still inside except* if it enters a loop self.return_stmt_inside_except_star_block: bool = False + self._object_type: Instance | None = None + self._str_type: Instance | None = None + self._function_type: Instance | None = None + # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties @property @@ -1241,7 +1245,7 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # This is a property. first_item.func.is_overload = True bare_setter_type = self.analyze_property_with_multi_part_definition(defn) - typ = function_type(first_item.func, self.named_type("builtins.function")) + typ = function_type(first_item.func, self.function_type()) assert isinstance(typ, CallableType) typ.definition = first_item types = [typ] @@ -1373,7 +1377,7 @@ def analyze_overload_sigs_and_impl( item.accept(self) # TODO: support decorated overloaded functions properly if isinstance(item, Decorator): - callable = function_type(item.func, self.named_type("builtins.function")) + callable = function_type(item.func, self.function_type()) assert isinstance(callable, CallableType) callable.definition = item if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators): @@ -1536,9 +1540,7 @@ def analyze_property_with_multi_part_definition( if first_node.name == "setter": # The first item represents the entire property. first_item.var.is_settable_property = True - setter_func_type = function_type( - item.func, self.named_type("builtins.function") - ) + setter_func_type = function_type(item.func, self.function_type()) assert isinstance(setter_func_type, CallableType) bare_setter_type = setter_func_type defn.setter_index = i + 1 @@ -6630,10 +6632,19 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non return result def object_type(self) -> Instance: - return self.named_type("builtins.object") + if self._object_type is None: + self._object_type = self.named_type("builtins.object") + return self._object_type def str_type(self) -> Instance: - return self.named_type("builtins.str") + if self._str_type is None: + self._str_type = self.named_type("builtins.str") + return self._str_type + + def function_type(self) -> Instance: + if self._function_type is None: + self._function_type = self.named_type("builtins.function") + return self._function_type def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance: sym = self.lookup_fully_qualified(fullname) From 83d17369d6db9b9378172139a0c89f0125baa0a3 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 8 Aug 2025 23:23:28 +0100 Subject: [PATCH 2/3] Tune the types --- mypy/checker.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6ae648077962..3256d63fb0c4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -433,7 +433,7 @@ def __init__( ) self._object_type: Instance | None = None - self._type_type: Instance | None = None + self._int_type: Instance | None = None self._str_type: Instance | None = None self._function_type: Instance | None = None @@ -7375,22 +7375,22 @@ def named_type(self, name: str) -> Instance: For example, named_type('builtins.object') produces the 'object' type. """ + if name == "builtins.str": + if self._str_type is None: + self._str_type = self._named_type(name) + return self._str_type if name == "builtins.function": if self._function_type is None: self._function_type = self._named_type(name) return self._function_type + if name == "builtins.int": + if self._int_type is None: + self._int_type = self._named_type(name) + return self._int_type if name == "builtins.object": if self._object_type is None: self._object_type = self._named_type(name) return self._object_type - if name == "builtins.type": - if self._type_type is None: - self._type_type = self._named_type(name) - return self._type_type - if name == "builtins.str": - if self._str_type is None: - self._str_type = self._named_type(name) - return self._str_type return self._named_type(name) def _named_type(self, name: str) -> Instance: From bb9682e0ee0dfdd4c38c6b0ddfb62c20625e3eba Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 9 Aug 2025 00:41:07 +0100 Subject: [PATCH 3/3] Cache bool --- mypy/checker.py | 9 +++++++-- mypy/checkexpr.py | 21 ++++++++++++++++----- mypy/semanal.py | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3256d63fb0c4..32ef3701df9e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -432,10 +432,11 @@ def __init__( self, self.msg, self.plugin, per_line_checking_time_ns ) - self._object_type: Instance | None = None - self._int_type: Instance | None = None self._str_type: Instance | None = None self._function_type: Instance | None = None + self._int_type: Instance | None = None + self._bool_type: Instance | None = None + self._object_type: Instance | None = None self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options) self._unique_id = 0 @@ -7387,6 +7388,10 @@ def named_type(self, name: str) -> Instance: if self._int_type is None: self._int_type = self._named_type(name) return self._int_type + if name == "builtins.bool": + if self._bool_type is None: + self._bool_type = self._named_type(name) + return self._bool_type if name == "builtins.object": if self._object_type is None: self._object_type = self._named_type(name) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 6e0915179f90..04ea678a2736 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -360,6 +360,9 @@ def __init__( ] = {} self.in_lambda_expr = False + self._literal_true: Instance | None = None + self._literal_false: Instance | None = None + def reset(self) -> None: self.resolved_type = {} self.expr_cache.clear() @@ -3428,11 +3431,19 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty if self.is_literal_context(): return LiteralType(value=value, fallback=typ) else: - return typ.copy_modified( - last_known_value=LiteralType( - value=value, fallback=typ, line=typ.line, column=typ.column - ) - ) + if value is True: + if self._literal_true is None: + self._literal_true = typ.copy_modified( + last_known_value=LiteralType(value=value, fallback=typ) + ) + return self._literal_true + if value is False: + if self._literal_false is None: + self._literal_false = typ.copy_modified( + last_known_value=LiteralType(value=value, fallback=typ) + ) + return self._literal_false + return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ)) def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType: """Concatenate two fixed length tuples.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 3e7a091e07c5..dfa210234606 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -497,9 +497,9 @@ def __init__( # Used to track edge case when return is still inside except* if it enters a loop self.return_stmt_inside_except_star_block: bool = False - self._object_type: Instance | None = None self._str_type: Instance | None = None self._function_type: Instance | None = None + self._object_type: Instance | None = None # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties