@@ -18,7 +18,7 @@ use crate::{
1818#[ derive( Debug ) ]
1919pub struct PySuper {
2020 #[ pymember( name = "__thisclass__" ) ]
21- typ : PyAtomicRef < PyType > ,
21+ typ : PyAtomicRef < Option < PyType > > ,
2222 #[ pymember( name = "__self__" ) ]
2323 obj : PyAtomicRef < Option < PyObject > > ,
2424 #[ pymember( name = "__self_class__" ) ]
@@ -38,22 +38,23 @@ fn bind_super(
3838}
3939
4040impl PySuper {
41- fn new_bound ( typ : PyTypeRef , obj : Option < PyObjectRef > , obj_type : Option < PyTypeRef > ) -> Self {
41+ fn empty ( ) -> Self {
4242 Self {
43- typ : PyAtomicRef :: from ( typ ) ,
44- obj : PyAtomicRef :: from ( obj ) ,
45- obj_type : PyAtomicRef :: from ( obj_type ) ,
43+ typ : PyAtomicRef :: from ( None ) ,
44+ obj : PyAtomicRef :: from ( None ) ,
45+ obj_type : PyAtomicRef :: from ( None ) ,
4646 }
4747 }
4848
49- fn store_bound ( & self , typ : PyTypeRef , obj : Option < PyObjectRef > , obj_type : Option < PyTypeRef > ) {
49+ fn store_bound (
50+ & self ,
51+ typ : Option < PyTypeRef > ,
52+ obj : Option < PyObjectRef > ,
53+ obj_type : Option < PyTypeRef > ,
54+ ) {
55+ drop ( self . typ . store ( typ) ) ;
5056 drop ( self . obj . store ( obj) ) ;
5157 drop ( self . obj_type . store ( obj_type) ) ;
52- drop ( self . typ . store ( typ) ) ;
53- }
54-
55- fn thisclass ( & self ) -> PyTypeRef {
56- self . typ . load_owned ( )
5758 }
5859}
5960
@@ -67,13 +68,9 @@ impl PyPayload for PySuper {
6768impl Constructor for PySuper {
6869 type Args = FuncArgs ;
6970
70- fn py_new ( _cls : & Py < PyType > , _args : Self :: Args , vm : & VirtualMachine ) -> PyResult < Self > {
71- let ( typ, obj, obj_type) = bind_super (
72- vm. ctx . types . object_type . to_owned ( ) , // is this correct?
73- vm. ctx . none ( ) ,
74- vm,
75- ) ?;
76- Ok ( Self :: new_bound ( typ, obj, obj_type) )
71+ fn py_new ( _cls : & Py < PyType > , _args : Self :: Args , _vm : & VirtualMachine ) -> PyResult < Self > {
72+ // `tp_new` leaves every field null. `tp_init` fills them in.
73+ Ok ( Self :: empty ( ) )
7774 }
7875}
7976
@@ -98,77 +95,93 @@ impl Initializer for PySuper {
9895 Self :: Args { py_type, object } : Self :: Args ,
9996 vm : & VirtualMachine ,
10097 ) -> PyResult < ( ) > {
101- // Get the type:
102- let ( typ, obj) = if let OptionalArg :: Present ( ty) = py_type {
103- ( ty, object. unwrap_or_none ( vm) )
104- } else {
105- // Access the InterpreterFrame directly — no need to materialize
106- // a FrameObject just to read code/locals.
107- let iframe_ptr = crate :: vm:: thread:: get_current_frame ( ) ;
108- if iframe_ptr. is_null ( ) {
109- return Err ( vm. new_runtime_error ( "super(): no current frame" ) ) ;
110- }
111- let iframe = unsafe { & * iframe_ptr } ;
112- let code = iframe. code ( ) ;
113-
114- if code. arg_count == 0 {
115- return Err ( vm. new_runtime_error ( "super(): no arguments" ) ) ;
116- }
98+ let typ = match py_type {
99+ OptionalArg :: Present ( ty) => Some ( ty) ,
100+ OptionalArg :: Missing => None ,
101+ } ;
102+ let obj = match object {
103+ OptionalArg :: Present ( obj) => Some ( obj) ,
104+ OptionalArg :: Missing => None ,
105+ } ;
106+ super_init_impl ( zelf, typ, obj, vm)
107+ }
108+ }
117109
118- // SAFETY: InterpreterFrame is current and not concurrently mutated.
119- use rustpython_compiler_core:: bytecode:: CO_FAST_CELL ;
120- let fastlocals = iframe. localsplus . fastlocals ( ) ;
121- let obj = fastlocals[ 0 ]
122- . clone ( )
123- . and_then ( |val| {
124- // If slot 0 is a merged cell (LOCAL|CELL), extract value from cell
125- if code
126- . localspluskinds
127- . first ( )
128- . is_some_and ( |& k| k & CO_FAST_CELL != 0 )
129- {
130- val. downcast_ref :: < PyCell > ( ) . and_then ( |c| c. get ( ) )
131- } else {
132- Some ( val)
133- }
134- } )
135- . ok_or_else ( || vm. new_runtime_error ( "super(): arg[0] deleted" ) ) ?;
136-
137- let mut typ = None ;
138- // Search for __class__ in freevars using localspluskinds
139- let nlocalsplus = code. localspluskinds . len ( ) ;
140- let nfrees = code. freevars . len ( ) ;
141- let free_start = nlocalsplus - nfrees;
142- for ( i, var) in code. freevars . iter ( ) . enumerate ( ) {
143- if var. as_bytes ( ) == b"__class__" {
144- let class = fastlocals[ free_start + i]
145- . as_ref ( )
146- . and_then ( |v| v. downcast_ref :: < PyCell > ( ) )
147- . and_then ( |c| c. get ( ) )
148- . ok_or_else ( || vm. new_runtime_error ( "super(): empty __class__ cell" ) ) ?;
149- typ = Some ( class. downcast ( ) . map_err ( |o| {
150- vm. new_type_error ( format ! (
151- "super(): __class__ is not a type ({})" ,
152- o. class( ) . name( )
153- ) )
154- } ) ?) ;
155- break ;
156- }
157- }
158- let typ = typ. ok_or_else ( || {
159- vm. new_type_error (
160- "super must be called with 1 argument or from inside class method" ,
161- )
162- } ) ?;
110+ /// `super_init_impl`. A null type takes the zero-argument path.
111+ fn super_init_impl (
112+ zelf : & Py < PySuper > ,
113+ typ : Option < PyTypeRef > ,
114+ obj : Option < PyObjectRef > ,
115+ vm : & VirtualMachine ,
116+ ) -> PyResult < ( ) > {
117+ let ( typ, obj) = match typ {
118+ Some ( typ) => ( typ, obj. unwrap_or_else ( || vm. ctx . none ( ) ) ) ,
119+ None => super_init_without_args ( vm) ?,
120+ } ;
121+ let ( typ, obj, obj_type) = bind_super ( typ, obj, vm) ?;
122+ zelf. store_bound ( Some ( typ) , obj, obj_type) ;
123+ Ok ( ( ) )
124+ }
163125
164- ( typ, obj)
165- } ;
126+ fn super_init_without_args ( vm : & VirtualMachine ) -> PyResult < ( PyTypeRef , PyObjectRef ) > {
127+ // Access the InterpreterFrame directly — no need to materialize
128+ // a FrameObject just to read code/locals.
129+ let iframe_ptr = crate :: vm:: thread:: get_current_frame ( ) ;
130+ if iframe_ptr. is_null ( ) {
131+ return Err ( vm. new_runtime_error ( "super(): no current frame" ) ) ;
132+ }
133+ let iframe = unsafe { & * iframe_ptr } ;
134+ let code = iframe. code ( ) ;
166135
167- let ( typ, obj, obj_type) = bind_super ( typ, obj, vm) ?;
168- zelf. store_bound ( typ, obj, obj_type) ;
136+ if code. arg_count == 0 {
137+ return Err ( vm. new_runtime_error ( "super(): no arguments" ) ) ;
138+ }
169139
170- Ok ( ( ) )
140+ // SAFETY: InterpreterFrame is current and not concurrently mutated.
141+ use rustpython_compiler_core:: bytecode:: CO_FAST_CELL ;
142+ let fastlocals = iframe. localsplus . fastlocals ( ) ;
143+ let obj = fastlocals[ 0 ]
144+ . clone ( )
145+ . and_then ( |val| {
146+ // If slot 0 is a merged cell (LOCAL|CELL), extract value from cell
147+ if code
148+ . localspluskinds
149+ . first ( )
150+ . is_some_and ( |& k| k & CO_FAST_CELL != 0 )
151+ {
152+ val. downcast_ref :: < PyCell > ( ) . and_then ( |c| c. get ( ) )
153+ } else {
154+ Some ( val)
155+ }
156+ } )
157+ . ok_or_else ( || vm. new_runtime_error ( "super(): arg[0] deleted" ) ) ?;
158+
159+ let mut typ = None ;
160+ // Search for __class__ in freevars using localspluskinds
161+ let nlocalsplus = code. localspluskinds . len ( ) ;
162+ let nfrees = code. freevars . len ( ) ;
163+ let free_start = nlocalsplus - nfrees;
164+ for ( i, var) in code. freevars . iter ( ) . enumerate ( ) {
165+ if var. as_bytes ( ) == b"__class__" {
166+ let class = fastlocals[ free_start + i]
167+ . as_ref ( )
168+ . and_then ( |v| v. downcast_ref :: < PyCell > ( ) )
169+ . and_then ( |c| c. get ( ) )
170+ . ok_or_else ( || vm. new_runtime_error ( "super(): empty __class__ cell" ) ) ?;
171+ typ = Some ( class. downcast ( ) . map_err ( |o| {
172+ vm. new_type_error ( format ! (
173+ "super(): __class__ is not a type ({})" ,
174+ o. class( ) . name( )
175+ ) )
176+ } ) ?) ;
177+ break ;
178+ }
171179 }
180+ let typ = typ. ok_or_else ( || {
181+ vm. new_type_error ( "super must be called with 1 argument or from inside class method" )
182+ } ) ?;
183+
184+ Ok ( ( typ, obj) )
172185}
173186
174187#[ pyclass(
@@ -200,7 +213,9 @@ impl GetAttr for PySuper {
200213 // Both locks are dropped before any arbitrary Python code
201214 // (the descriptor call below) runs, so they can't be held
202215 // across a call that might re-enter and want them again.
203- let su_type = zelf. thisclass ( ) ;
216+ let Some ( su_type) = zelf. typ . load_owned ( ) else {
217+ return skip ( zelf, name) ;
218+ } ;
204219 let descr = {
205220 let mro = start_type. mro . read ( ) ;
206221 mro. iter ( )
@@ -239,20 +254,30 @@ impl GetDescriptor for PySuper {
239254 return Ok ( zelf_obj. to_owned ( ) ) ;
240255 }
241256 let zelf_class = zelf. as_object ( ) . class ( ) ;
242- let typ = zelf. thisclass ( ) ;
257+ let typ = zelf. typ . load_owned ( ) ;
243258 if zelf_class. is ( vm. ctx . types . super_type ) {
244- let ( typ, obj, obj_type) = bind_super ( typ, obj. to_owned ( ) , vm) ?;
245- Ok ( Self :: new_bound ( typ, obj, obj_type) . into_ref ( & vm. ctx ) . into ( ) )
259+ let newobj = Self :: empty ( ) . into_ref ( & vm. ctx ) ;
260+ // A null type takes the zero-argument path inside super_init_impl.
261+ super_init_impl ( & newobj, typ, Some ( obj. to_owned ( ) ) , vm) ?;
262+ Ok ( newobj. into ( ) )
246263 } else {
247- PyType :: call ( zelf. class ( ) , ( typ, obj. to_owned ( ) ) . into_args ( vm) , vm)
264+ // Call stops at the first null argument.
265+ let args = match typ {
266+ Some ( typ) => ( typ, obj. to_owned ( ) ) . into_args ( vm) ,
267+ None => FuncArgs :: default ( ) ,
268+ } ;
269+ PyType :: call ( zelf. class ( ) , args, vm)
248270 }
249271 }
250272}
251273
252274impl Representable for PySuper {
253275 #[ inline]
254276 fn repr_str ( zelf : & Py < Self > , _vm : & VirtualMachine ) -> PyResult < String > {
255- let type_name = zelf. thisclass ( ) . name ( ) . to_owned ( ) ;
277+ let type_name = match zelf. typ . load_owned ( ) {
278+ Some ( ty) => ty. name ( ) . to_owned ( ) ,
279+ None => "NULL" . to_owned ( ) ,
280+ } ;
256281 let repr = match zelf. obj_type . load_owned ( ) {
257282 Some ( ty) => format ! ( "<super: <class '{}'>, <{} object>>" , type_name, ty. name( ) ) ,
258283 None => format ! ( "<super: <class '{type_name}'>, NULL>" ) ,
0 commit comments