12
12
from mypy .server .trigger import make_trigger
13
13
from mypy .types import Instance , Type , TypeVarId , get_proper_type
14
14
15
+ MAX_NEGATIVE_CACHE_TYPES : Final = 1000
16
+ MAX_NEGATIVE_CACHE_ENTRIES : Final = 10000
17
+
15
18
# Represents that the 'left' instance is a subtype of the 'right' instance
16
19
SubtypeRelationship : _TypeAlias = Tuple [Instance , Instance ]
17
20
@@ -42,6 +45,9 @@ class TypeState:
42
45
# We need the caches, since subtype checks for structural types are very slow.
43
46
_subtype_caches : Final [SubtypeCache ]
44
47
48
+ # Same as above but for negative subtyping results.
49
+ _negative_subtype_caches : Final [SubtypeCache ]
50
+
45
51
# This contains protocol dependencies generated after running a full build,
46
52
# or after an update. These dependencies are special because:
47
53
# * They are a global property of the program; i.e. some dependencies for imported
@@ -95,6 +101,7 @@ class TypeState:
95
101
96
102
def __init__ (self ) -> None :
97
103
self ._subtype_caches = {}
104
+ self ._negative_subtype_caches = {}
98
105
self .proto_deps = {}
99
106
self ._attempted_protocols = {}
100
107
self ._checked_against_members = {}
@@ -128,11 +135,14 @@ def get_assumptions(self, is_proper: bool) -> list[tuple[Type, Type]]:
128
135
def reset_all_subtype_caches (self ) -> None :
129
136
"""Completely reset all known subtype caches."""
130
137
self ._subtype_caches .clear ()
138
+ self ._negative_subtype_caches .clear ()
131
139
132
140
def reset_subtype_caches_for (self , info : TypeInfo ) -> None :
133
141
"""Reset subtype caches (if any) for a given supertype TypeInfo."""
134
142
if info in self ._subtype_caches :
135
143
self ._subtype_caches [info ].clear ()
144
+ if info in self ._negative_subtype_caches :
145
+ self ._negative_subtype_caches [info ].clear ()
136
146
137
147
def reset_all_subtype_caches_for (self , info : TypeInfo ) -> None :
138
148
"""Reset subtype caches (if any) for a given supertype TypeInfo and its MRO."""
@@ -154,6 +164,23 @@ def is_cached_subtype_check(self, kind: SubtypeKind, left: Instance, right: Inst
154
164
return False
155
165
return (left , right ) in subcache
156
166
167
+ def is_cached_negative_subtype_check (
168
+ self , kind : SubtypeKind , left : Instance , right : Instance
169
+ ) -> bool :
170
+ if left .last_known_value is not None or right .last_known_value is not None :
171
+ # If there is a literal last known value, give up. There
172
+ # will be an unbounded number of potential types to cache,
173
+ # making caching less effective.
174
+ return False
175
+ info = right .type
176
+ cache = self ._negative_subtype_caches .get (info )
177
+ if cache is None :
178
+ return False
179
+ subcache = cache .get (kind )
180
+ if subcache is None :
181
+ return False
182
+ return (left , right ) in subcache
183
+
157
184
def record_subtype_cache_entry (
158
185
self , kind : SubtypeKind , left : Instance , right : Instance
159
186
) -> None :
@@ -164,6 +191,21 @@ def record_subtype_cache_entry(
164
191
cache = self ._subtype_caches .setdefault (right .type , dict ())
165
192
cache .setdefault (kind , set ()).add ((left , right ))
166
193
194
+ def record_negative_subtype_cache_entry (
195
+ self , kind : SubtypeKind , left : Instance , right : Instance
196
+ ) -> None :
197
+ if left .last_known_value is not None or right .last_known_value is not None :
198
+ # These are unlikely to match, due to the large space of
199
+ # possible values. Avoid uselessly increasing cache sizes.
200
+ return
201
+ if len (self ._negative_subtype_caches ) > MAX_NEGATIVE_CACHE_TYPES :
202
+ self ._negative_subtype_caches .clear ()
203
+ cache = self ._negative_subtype_caches .setdefault (right .type , dict ())
204
+ subcache = cache .setdefault (kind , set ())
205
+ if len (subcache ) > MAX_NEGATIVE_CACHE_ENTRIES :
206
+ subcache .clear ()
207
+ cache .setdefault (kind , set ()).add ((left , right ))
208
+
167
209
def reset_protocol_deps (self ) -> None :
168
210
"""Reset dependencies after a full run or before a daemon shutdown."""
169
211
self .proto_deps = {}
0 commit comments