1
- from typing import (Dict , List , Set , Iterator )
1
+ from typing import (Dict , List , Set , Iterator , Union , cast )
2
2
from contextlib import contextmanager
3
3
4
4
from mypy .types import Type , AnyType , PartialType
9
9
from mypy .sametypes import is_same_type
10
10
11
11
12
- class Frame (Dict [Key , Type ]):
12
+ class Deleted :
13
+ """Frame entry representing a deleted variable."""
14
+
15
+ source = '' # May be None; name that generated this value
16
+
17
+ def __init__ (self , source : str , as_lvalue : bool = False ) -> None :
18
+ self .source = source
19
+ self .as_lvalue = as_lvalue
20
+
21
+
22
+ # What's known about an expression at a specific moment:
23
+ # either its current type, or that it has been deleted.
24
+ Status = Union [Type , Deleted ]
25
+
26
+
27
+ class Frame (Dict [Key , Status ]):
13
28
"""A Frame represents a specific point in the execution of a program.
14
29
It carries information about the current types of expressions at
15
30
that point, arising either from assignments to those expressions
@@ -64,7 +79,7 @@ def __init__(self) -> None:
64
79
65
80
# Maps expr.literal_hash] to get_declaration(expr)
66
81
# for every expr stored in the binder
67
- self .declarations = Frame ()
82
+ self .declarations = {} # type: Dict[Key, Type]
68
83
# Set of other keys to invalidate if a key is changed, e.g. x -> {x.a, x[0]}
69
84
# Whenever a new key (e.g. x.a.b) is added, we update this
70
85
self .dependencies = {} # type: Dict[Key, Set[Key]]
@@ -92,18 +107,18 @@ def push_frame(self) -> Frame:
92
107
self .options_on_return .append ([])
93
108
return f
94
109
95
- def _push (self , key : Key , type : Type , index : int = - 1 ) -> None :
110
+ def _push (self , key : Key , type : Status , index : int = - 1 ) -> None :
96
111
self .frames [index ][key ] = type
97
112
98
- def _get (self , key : Key , index : int = - 1 ) -> Type :
113
+ def _get (self , key : Key , index : int = - 1 ) -> Status :
99
114
if index < 0 :
100
115
index += len (self .frames )
101
116
for i in range (index , - 1 , - 1 ):
102
117
if key in self .frames [i ]:
103
118
return self .frames [i ][key ]
104
119
return None
105
120
106
- def push (self , expr : Node , typ : Type ) -> None :
121
+ def push (self , expr : Node , typ : Status ) -> None :
107
122
if not expr .literal :
108
123
return
109
124
key = expr .literal_hash
@@ -112,12 +127,23 @@ def push(self, expr: Node, typ: Type) -> None:
112
127
self ._add_dependencies (key )
113
128
self ._push (key , typ )
114
129
130
+ def not_deleted (self , expr : Node ) -> None :
131
+ if not expr .literal :
132
+ return
133
+ if isinstance (self .get (expr ), Deleted ):
134
+ self .push (expr , self .get_declaration (expr ))
135
+
115
136
def unreachable (self ) -> None :
116
137
self .frames [- 1 ].unreachable = True
117
138
118
- def get (self , expr : Node ) -> Type :
139
+ def get (self , expr : Node ) -> Status :
119
140
return self ._get (expr .literal_hash )
120
141
142
+ def get_lvalue (self , expr : Node ) -> Status :
143
+ if expr .literal :
144
+ return self .declarations .get (expr .literal_hash )
145
+ return None
146
+
121
147
def is_unreachable (self ) -> bool :
122
148
# TODO: Copy the value of unreachable into new frames to avoid
123
149
# this traversal on every statement?
@@ -148,21 +174,28 @@ def update_from_options(self, frames: List[Frame]) -> bool:
148
174
for key in keys :
149
175
current_value = self ._get (key )
150
176
resulting_values = [f .get (key , current_value ) for f in frames ]
177
+
151
178
if any (x is None for x in resulting_values ):
152
179
# We didn't know anything about key before
153
180
# (current_value must be None), and we still don't
154
181
# know anything about key in at least one possible frame.
182
+ # It might be deleted though.
183
+ deleted_values = [x for x in resulting_values if isinstance (x , Deleted )]
184
+ if deleted_values :
185
+ self ._push (key , deleted_values [0 ])
186
+ changed = True
155
187
continue
156
188
157
- if isinstance (self .declarations .get (key ), AnyType ):
189
+ declaration = self .declarations .get (key )
190
+ if isinstance (declaration , AnyType ):
158
191
type = resulting_values [0 ]
159
- if not all (is_same_type (type , t ) for t in resulting_values [1 :]):
192
+ if not all (is_same_status (type , t ) for t in resulting_values [1 :]):
160
193
type = AnyType ()
161
194
else :
162
195
type = resulting_values [0 ]
163
196
for other in resulting_values [1 :]:
164
- type = join_simple ( self . declarations [ key ] , type , other )
165
- if not is_same_type (type , current_value ):
197
+ type = join_status ( declaration , type , other )
198
+ if not is_same_status (type , current_value ):
166
199
self ._push (key , type )
167
200
changed = True
168
201
@@ -199,7 +232,7 @@ def get_declaration(self, expr: Node) -> Type:
199
232
return None
200
233
201
234
def assign_type (self , expr : Node ,
202
- type : Type ,
235
+ type : Status ,
203
236
declared_type : Type ,
204
237
restrict_any : bool = False ) -> None :
205
238
if not expr .literal :
@@ -210,7 +243,7 @@ def assign_type(self, expr: Node,
210
243
# Not sure why this happens. It seems to mainly happen in
211
244
# member initialization.
212
245
return
213
- if not is_subtype (type , declared_type ):
246
+ if isinstance ( type , Type ) and not is_subtype (type , declared_type ):
214
247
# Pretty sure this is only happens when there's a type error.
215
248
216
249
# Ideally this function wouldn't be called if the
@@ -247,13 +280,13 @@ def invalidate_dependencies(self, expr: Node) -> None:
247
280
for dep in self .dependencies .get (expr .literal_hash , set ()):
248
281
self ._cleanse_key (dep )
249
282
250
- def most_recent_enclosing_type (self , expr : Node , type : Type ) -> Type :
283
+ def most_recent_enclosing_type (self , expr : Node , type : Status ) -> Status :
251
284
if isinstance (type , AnyType ):
252
285
return self .get_declaration (expr )
253
286
key = expr .literal_hash
254
- enclosers = ([self .get_declaration (expr )] +
287
+ enclosers = ([cast ( Status , self .get_declaration (expr ) )] +
255
288
[f [key ] for f in self .frames
256
- if key in f and is_subtype ( type , f [ key ]) ])
289
+ if key in f ])
257
290
return enclosers [- 1 ]
258
291
259
292
def allow_jump (self , index : int ) -> None :
@@ -335,3 +368,20 @@ def top_frame_context(self) -> Iterator[Frame]:
335
368
assert len (self .frames ) == 1
336
369
yield self .push_frame ()
337
370
self .pop_frame (True , 0 )
371
+
372
+
373
+ def join_status (declaration : Type , s : Status , t : Status ) -> Status :
374
+ if isinstance (s , Deleted ):
375
+ return s
376
+
377
+ if isinstance (t , Deleted ):
378
+ return t
379
+
380
+ return join_simple (declaration , s , t )
381
+
382
+
383
+ def is_same_status (s : Status , t : Status ) -> bool :
384
+ if isinstance (s , Deleted ) or isinstance (t , Deleted ):
385
+ return isinstance (s , Deleted ) and isinstance (t , Deleted ) and s .as_lvalue == t .as_lvalue
386
+
387
+ return is_same_type (s , t )
0 commit comments