From ec4404ae331170f6d033f442906a547dec319e02 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 28 Dec 2022 14:24:07 +0000 Subject: [PATCH] Optimization: Enable always defined attributes in Type subclasses (#14356) Use lazy initialization to avoid method calls in `__init__`. This allows mypyc to infer more always defined attributes. (Various small optimizations, including this, together netted a 6% performance improvement in self check.) --- mypy/types.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 83c5b88032a3..b48280466c3e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -201,7 +201,7 @@ def deserialize_type(data: JsonDict | str) -> Type: class Type(mypy.nodes.Context): """Abstract base class for all types.""" - __slots__ = ("can_be_true", "can_be_false") + __slots__ = ("_can_be_true", "_can_be_false") # 'can_be_true' and 'can_be_false' mean whether the value of the # expression can be true or false in a boolean context. They are useful # when inferring the type of logic expressions like `x and y`. @@ -214,8 +214,29 @@ class Type(mypy.nodes.Context): def __init__(self, line: int = -1, column: int = -1) -> None: super().__init__(line, column) - self.can_be_true = self.can_be_true_default() - self.can_be_false = self.can_be_false_default() + # Value of these can be -1 (use the default, lazy init), 0 (false) or 1 (true) + self._can_be_true = -1 + self._can_be_false = -1 + + @property + def can_be_true(self) -> bool: + if self._can_be_true == -1: # Lazy init helps mypyc + self._can_be_true = self.can_be_true_default() + return bool(self._can_be_true) + + @can_be_true.setter + def can_be_true(self, v: bool) -> None: + self._can_be_true = v + + @property + def can_be_false(self) -> bool: + if self._can_be_false == -1: # Lazy init helps mypyc + self._can_be_false = self.can_be_false_default() + return bool(self._can_be_false) + + @can_be_false.setter + def can_be_false(self, v: bool) -> None: + self._can_be_false = v def can_be_true_default(self) -> bool: return True @@ -264,10 +285,10 @@ def __init__( line: int = -1, column: int = -1, ) -> None: + super().__init__(line, column) self.alias = alias self.args = args self.type_ref: str | None = None - super().__init__(line, column) def _expand_once(self) -> Type: """Expand to the target type exactly once. @@ -1424,7 +1445,7 @@ class FunctionLike(ProperType): def __init__(self, line: int = -1, column: int = -1) -> None: super().__init__(line, column) - self.can_be_false = False + self._can_be_false = False @abstractmethod def is_type_obj(self) -> bool: @@ -2183,10 +2204,10 @@ def __init__( column: int = -1, implicit: bool = False, ) -> None: + super().__init__(line, column) self.partial_fallback = fallback self.items = items self.implicit = implicit - super().__init__(line, column) def can_be_true_default(self) -> bool: if self.can_be_any_bool(): @@ -2495,8 +2516,8 @@ class LiteralType(ProperType): def __init__( self, value: LiteralValue, fallback: Instance, line: int = -1, column: int = -1 ) -> None: - self.value = value super().__init__(line, column) + self.value = value self.fallback = fallback self._hash = -1 # Cached hash value