@@ -123,6 +123,19 @@ def unwrap_all(xs):
123123 return [Const .unwrap (x ) for x in xs ]
124124
125125
126+ class _TypeInCell (object ):
127+ """Marker for an inferred type stored in a synthetic closure cell.
128+
129+ When MAKE_FUNCTION is emulated, the closure cells of the function it
130+ creates are populated with the inferred types of the captured variables
131+ rather than with actual runtime values. This wrapper marks such cells so
132+ that FrameState.closure_type can distinguish them from the cells of a real
133+ closure, which hold runtime values.
134+ """
135+ def __init__ (self , value ):
136+ self .value = value
137+
138+
126139class FrameState (object ):
127140 """Stores the state of the frame at a particular point of execution.
128141 """
@@ -145,20 +158,42 @@ def copy(self):
145158 def const_type (self , i ):
146159 return Const (self .co .co_consts [i ])
147160
148- def get_closure (self , i ):
149- num_cellvars = len (self .co .co_cellvars )
150- if i < num_cellvars :
151- return self .vars [i ]
152- else :
153- return self .f .__closure__ [i - num_cellvars ].cell_contents
154-
155161 def closure_type (self , i ):
156- """Returns a TypeConstraint or Const."""
157- val = self .get_closure (i )
158- if isinstance (val , typehints .TypeConstraint ):
159- return val
162+ """Returns the type of the cell or free variable with the given index.
163+
164+ The index is the raw oparg of a LOAD_CLOSURE or LOAD_DEREF instruction.
165+ For Python < 3.11 it indexes co_cellvars + co_freevars. From Python 3.11
166+ on it indexes the frame's "fast locals" (localsplus) storage, in which
167+ the cell of a captured parameter shares the slot of the parameter
168+ itself, so the slot layout is co_varnames, followed by the cell
169+ variables that are not parameters, followed by the free variables.
170+ """
171+ if sys .version_info >= (3 , 11 ):
172+ names = self .co .co_varnames + tuple (
173+ c for c in self .co .co_cellvars
174+ if c not in self .co .co_varnames ) + self .co .co_freevars
160175 else :
176+ names = self .co .co_cellvars + self .co .co_freevars
177+ name = names [i ]
178+ if name in self .co .co_freevars :
179+ # A free variable: its cell belongs to the function's closure.
180+ val = self .f .__closure__ [self .co .co_freevars .index (name )].cell_contents
181+ if isinstance (val , _TypeInCell ):
182+ # A synthetic cell produced while emulating MAKE_FUNCTION: it holds
183+ # the inferred type of the captured variable, not an actual runtime
184+ # value.
185+ return val .value
161186 return Const (val )
187+ try :
188+ # A cell variable of the current frame. The frame state tracks the
189+ # inferred *type* of each local variable rather than its value, so the
190+ # tracked type can be returned directly.
191+ return self .vars [self .co .co_varnames .index (name )]
192+ except ValueError :
193+ # The cell variable does not correspond to a tracked local variable
194+ # (e.g. it is only ever assigned via STORE_DEREF, which is not
195+ # modeled), so its type is unknown.
196+ return typehints .Any
162197
163198 def get_global (self , i ):
164199 name = self .get_name (i )
@@ -468,13 +503,15 @@ def infer_return_type_func(f, input_types, debug=False, depth=0):
468503 print ('(' + dis .cmp_op [arg ] + ')' , end = ' ' )
469504 elif op in dis .hasfree :
470505 if free is None :
471- free = co .co_cellvars + co .co_freevars
472- # From 3.11 on the arg is no longer offset by len(co_varnames)
473- # so we adjust it back
474- print_arg = arg
475- if (sys .version_info .major , sys .version_info .minor ) >= (3 , 11 ):
476- print_arg = arg - len (co .co_varnames )
477- print ('(' + free [print_arg ] + ')' , end = ' ' )
506+ # From 3.11 on the arg indexes the localsplus storage, in which
507+ # the cell of a captured parameter shares the parameter's slot.
508+ if (sys .version_info .major , sys .version_info .minor ) >= (3 , 11 ):
509+ free = co .co_varnames + tuple (
510+ c for c in co .co_cellvars
511+ if c not in co .co_varnames ) + co .co_freevars
512+ else :
513+ free = co .co_cellvars + co .co_freevars
514+ print ('(' + free [arg ] + ')' , end = ' ' )
478515
479516 # Actually emulate the op.
480517 if state is None and states [start ] is None :
0 commit comments