@@ -2069,66 +2069,56 @@ impl Compiler<'_> {
20692069 false
20702070 }
20712071
2072- fn compile_class_def (
2072+ /// Compile the class body into a code object
2073+ /// This is similar to CPython's compiler_class_body
2074+ fn compile_class_body (
20732075 & mut self ,
20742076 name : & str ,
20752077 body : & [ Stmt ] ,
2076- decorator_list : & [ Decorator ] ,
20772078 type_params : Option < & TypeParams > ,
2078- arguments : Option < & Arguments > ,
2079- ) -> CompileResult < ( ) > {
2080- self . prepare_decorators ( decorator_list) ?;
2081-
2082- let prev_ctx = self . ctx ;
2083- self . ctx = CompileContext {
2084- func : FunctionContext :: NoFunction ,
2085- in_class : true ,
2086- loop_data : None ,
2087- } ;
2088-
2089- // If there are type params, we need to push a special symbol table just for them
2090- if let Some ( type_params) = type_params {
2091- self . push_symbol_table ( ) ;
2092- // Save current private name to restore later
2093- let saved_private = self . code_stack . last ( ) . and_then ( |info| info. private . clone ( ) ) ;
2094- // Compile type parameters and store as .type_params
2095- self . compile_type_params ( type_params) ?;
2096- // Restore private name after type param scope
2097- if let Some ( private) = saved_private {
2098- self . code_stack . last_mut ( ) . unwrap ( ) . private = Some ( private) ;
2099- }
2100- let dot_type_params = self . name ( ".type_params" ) ;
2101- emit ! ( self , Instruction :: StoreLocal ( dot_type_params) ) ;
2102- }
2103-
2104- self . push_output ( bytecode:: CodeFlags :: empty ( ) , 0 , 0 , 0 , name. to_owned ( ) ) ;
2079+ firstlineno : u32 ,
2080+ ) -> CompileResult < CodeObject > {
2081+ // 1. Enter class scope
2082+ // Use enter_scope instead of push_output to match CPython
2083+ let key = self . symbol_table_stack . len ( ) ;
2084+ self . push_symbol_table ( ) ;
2085+ self . enter_scope ( name, SymbolTableType :: Class , key, firstlineno) ?;
21052086
21062087 // Set qualname using the new method
21072088 let qualname = self . set_qualname ( ) ;
21082089
21092090 // For class scopes, set u_private to the class name for name mangling
21102091 self . code_stack . last_mut ( ) . unwrap ( ) . private = Some ( name. to_owned ( ) ) ;
21112092
2093+ // 2. Set up class namespace
21122094 let ( doc_str, body) = split_doc ( body, & self . opts ) ;
21132095
2096+ // Load (global) __name__ and store as __module__
21142097 let dunder_name = self . name ( "__name__" ) ;
21152098 emit ! ( self , Instruction :: LoadGlobal ( dunder_name) ) ;
21162099 let dunder_module = self . name ( "__module__" ) ;
21172100 emit ! ( self , Instruction :: StoreLocal ( dunder_module) ) ;
2101+
2102+ // Store __qualname__
21182103 self . emit_load_const ( ConstantData :: Str {
21192104 value : qualname. into ( ) ,
21202105 } ) ;
21212106 let qualname_name = self . name ( "__qualname__" ) ;
21222107 emit ! ( self , Instruction :: StoreLocal ( qualname_name) ) ;
2108+
2109+ // Store __doc__
21232110 self . load_docstring ( doc_str) ;
21242111 let doc = self . name ( "__doc__" ) ;
21252112 emit ! ( self , Instruction :: StoreLocal ( doc) ) ;
2126- // setup annotations
2127- if Self :: find_ann ( body) {
2128- emit ! ( self , Instruction :: SetupAnnotation ) ;
2129- }
21302113
2131- // Set __type_params__ from .type_params if we have type parameters (PEP 695)
2114+ // Store __firstlineno__ (new in Python 3.12+)
2115+ self . emit_load_const ( ConstantData :: Integer {
2116+ value : BigInt :: from ( firstlineno) ,
2117+ } ) ;
2118+ let firstlineno_name = self . name ( "__firstlineno__" ) ;
2119+ emit ! ( self , Instruction :: StoreLocal ( firstlineno_name) ) ;
2120+
2121+ // Set __type_params__ if we have type parameters
21322122 if type_params. is_some ( ) {
21332123 // Load .type_params from enclosing scope
21342124 let dot_type_params = self . name ( ".type_params" ) ;
@@ -2139,8 +2129,15 @@ impl Compiler<'_> {
21392129 emit ! ( self , Instruction :: StoreLocal ( dunder_type_params) ) ;
21402130 }
21412131
2132+ // Setup annotations if needed
2133+ if Self :: find_ann ( body) {
2134+ emit ! ( self , Instruction :: SetupAnnotation ) ;
2135+ }
2136+
2137+ // 3. Compile the class body
21422138 self . compile_statements ( body) ?;
21432139
2140+ // 4. Handle __classcell__ if needed
21442141 let classcell_idx = self
21452142 . code_stack
21462143 . last_mut ( )
@@ -2159,65 +2156,167 @@ impl Compiler<'_> {
21592156 self . emit_load_const ( ConstantData :: None ) ;
21602157 }
21612158
2159+ // Return the class namespace
21622160 self . emit_return_value ( ) ;
21632161
2164- let code = self . exit_scope ( ) ;
2165- self . ctx = prev_ctx;
2162+ // Exit scope and return the code object
2163+ Ok ( self . exit_scope ( ) )
2164+ }
2165+
2166+ fn compile_class_def (
2167+ & mut self ,
2168+ name : & str ,
2169+ body : & [ Stmt ] ,
2170+ decorator_list : & [ Decorator ] ,
2171+ type_params : Option < & TypeParams > ,
2172+ arguments : Option < & Arguments > ,
2173+ ) -> CompileResult < ( ) > {
2174+ self . prepare_decorators ( decorator_list) ?;
21662175
2167- emit ! ( self , Instruction :: LoadBuildClass ) ;
2176+ let is_generic = type_params. is_some ( ) ;
2177+ let firstlineno = self . get_source_line_number ( ) . get ( ) . to_u32 ( ) ;
21682178
2169- let mut func_flags = bytecode:: MakeFunctionFlags :: empty ( ) ;
2179+ // Step 1: If generic, enter type params scope and compile type params
2180+ if is_generic {
2181+ let type_params_name = format ! ( "<generic parameters of {name}>" ) ;
2182+ self . push_output (
2183+ bytecode:: CodeFlags :: IS_OPTIMIZED | bytecode:: CodeFlags :: NEW_LOCALS ,
2184+ 0 ,
2185+ 0 ,
2186+ 0 ,
2187+ type_params_name,
2188+ ) ;
21702189
2171- // Prepare generic type parameters:
2172- if type_params. is_some ( ) {
2173- // Load .type_params from the type params scope
2190+ // Set private name for name mangling
2191+ self . code_stack . last_mut ( ) . unwrap ( ) . private = Some ( name. to_owned ( ) ) ;
2192+
2193+ // Compile type parameters and store as .type_params
2194+ self . compile_type_params ( type_params. unwrap ( ) ) ?;
21742195 let dot_type_params = self . name ( ".type_params" ) ;
2175- emit ! ( self , Instruction :: LoadNameAny ( dot_type_params) ) ;
2176- func_flags |= bytecode:: MakeFunctionFlags :: TYPE_PARAMS ;
2196+ emit ! ( self , Instruction :: StoreLocal ( dot_type_params) ) ;
21772197 }
21782198
2179- if self . build_closure ( & code) {
2180- func_flags |= bytecode:: MakeFunctionFlags :: CLOSURE ;
2181- }
2199+ // Step 2: Compile class body (always done, whether generic or not)
2200+ let prev_ctx = self . ctx ;
2201+ self . ctx = CompileContext {
2202+ func : FunctionContext :: NoFunction ,
2203+ in_class : true ,
2204+ loop_data : None ,
2205+ } ;
2206+ let class_code = self . compile_class_body ( name, body, type_params, firstlineno) ?;
2207+ self . ctx = prev_ctx;
21822208
2183- self . emit_load_const ( ConstantData :: Code {
2184- code : Box :: new ( code) ,
2185- } ) ;
2186- self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2209+ // Step 3: Generate the rest of the code for the call
2210+ if is_generic {
2211+ // Still in type params scope
2212+ let dot_type_params = self . name ( ".type_params" ) ;
2213+ let dot_generic_base = self . name ( ".generic_base" ) ;
21872214
2188- // Turn code object into function object:
2189- emit ! ( self , Instruction :: MakeFunction ( func_flags) ) ;
2215+ // Create .generic_base
2216+ emit ! ( self , Instruction :: LoadNameAny ( dot_type_params) ) ;
2217+ emit ! (
2218+ self ,
2219+ Instruction :: CallIntrinsic1 {
2220+ func: bytecode:: IntrinsicFunction1 :: SubscriptGeneric
2221+ }
2222+ ) ;
2223+ emit ! ( self , Instruction :: StoreLocal ( dot_generic_base) ) ;
21902224
2191- self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2225+ // Generate class creation code
2226+ emit ! ( self , Instruction :: LoadBuildClass ) ;
21922227
2193- // For PEP 695 classes: handle Generic base creation
2194- if type_params. is_some ( ) {
2195- if let Some ( arguments) = arguments {
2196- // Has explicit bases - use them as is, don't add Generic
2197- // CPython doesn't add Generic when explicit bases are present
2198- let call = self . compile_call_inner ( 2 , arguments) ?;
2199- self . compile_normal_call ( call) ;
2228+ // Set up the class function with type params
2229+ let mut func_flags = bytecode:: MakeFunctionFlags :: empty ( ) ;
2230+ emit ! ( self , Instruction :: LoadNameAny ( dot_type_params) ) ;
2231+ func_flags |= bytecode:: MakeFunctionFlags :: TYPE_PARAMS ;
2232+
2233+ if self . build_closure ( & class_code) {
2234+ func_flags |= bytecode:: MakeFunctionFlags :: CLOSURE ;
2235+ }
2236+
2237+ self . emit_load_const ( ConstantData :: Code {
2238+ code : Box :: new ( class_code) ,
2239+ } ) ;
2240+ self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2241+ emit ! ( self , Instruction :: MakeFunction ( func_flags) ) ;
2242+ self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2243+
2244+ // Compile original bases
2245+ let base_count = if let Some ( arguments) = arguments {
2246+ for arg in & arguments. args {
2247+ self . compile_expression ( arg) ?;
2248+ }
2249+ arguments. args . len ( )
22002250 } else {
2201- // No explicit bases, add Generic[*type_params] as the only base
2202- // Stack currently: [function, class_name]
2251+ 0
2252+ } ;
2253+
2254+ // Load .generic_base as the last base
2255+ emit ! ( self , Instruction :: LoadNameAny ( dot_generic_base) ) ;
22032256
2204- // Load .type_params for creating Generic base
2205- let dot_type_params = self . name ( ".type_params" ) ;
2206- emit ! ( self , Instruction :: LoadNameAny ( dot_type_params) ) ;
2257+ let nargs = 2 + u32:: try_from ( base_count) . expect ( "too many base classes" ) + 1 ; // function, name, bases..., generic_base
22072258
2208- // Call INTRINSIC_SUBSCRIPT_GENERIC to create Generic[*type_params]
2259+ // Handle keyword arguments
2260+ if let Some ( arguments) = arguments
2261+ && !arguments. keywords . is_empty ( )
2262+ {
2263+ for keyword in & arguments. keywords {
2264+ if let Some ( name) = & keyword. arg {
2265+ self . emit_load_const ( ConstantData :: Str {
2266+ value : name. as_str ( ) . into ( ) ,
2267+ } ) ;
2268+ }
2269+ self . compile_expression ( & keyword. value ) ?;
2270+ }
22092271 emit ! (
22102272 self ,
2211- Instruction :: CallIntrinsic1 {
2212- func: bytecode:: IntrinsicFunction1 :: SubscriptGeneric
2273+ Instruction :: CallFunctionKeyword {
2274+ nargs: nargs
2275+ + u32 :: try_from( arguments. keywords. len( ) )
2276+ . expect( "too many keyword arguments" )
22132277 }
22142278 ) ;
2279+ } else {
2280+ emit ! ( self , Instruction :: CallFunctionPositional { nargs } ) ;
2281+ }
2282+
2283+ // Return the created class
2284+ self . emit_return_value ( ) ;
22152285
2216- // Call __build_class__ with 3 positional args: function, class_name, Generic[T]
2217- emit ! ( self , Instruction :: CallFunctionPositional { nargs: 3 } ) ;
2286+ // Exit type params scope and wrap in function
2287+ let type_params_code = self . exit_scope ( ) ;
2288+
2289+ // Execute the type params function
2290+ if self . build_closure ( & type_params_code) {
2291+ // Should not need closure
22182292 }
2293+ self . emit_load_const ( ConstantData :: Code {
2294+ code : Box :: new ( type_params_code) ,
2295+ } ) ;
2296+ self . emit_load_const ( ConstantData :: Str {
2297+ value : format ! ( "<generic parameters of {name}>" ) . into ( ) ,
2298+ } ) ;
2299+ emit ! (
2300+ self ,
2301+ Instruction :: MakeFunction ( bytecode:: MakeFunctionFlags :: empty( ) )
2302+ ) ;
2303+ emit ! ( self , Instruction :: CallFunctionPositional { nargs: 0 } ) ;
22192304 } else {
2220- // No type params, normal compilation
2305+ // Non-generic class: standard path
2306+ emit ! ( self , Instruction :: LoadBuildClass ) ;
2307+
2308+ let mut func_flags = bytecode:: MakeFunctionFlags :: empty ( ) ;
2309+ if self . build_closure ( & class_code) {
2310+ func_flags |= bytecode:: MakeFunctionFlags :: CLOSURE ;
2311+ }
2312+
2313+ self . emit_load_const ( ConstantData :: Code {
2314+ code : Box :: new ( class_code) ,
2315+ } ) ;
2316+ self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2317+ emit ! ( self , Instruction :: MakeFunction ( func_flags) ) ;
2318+ self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
2319+
22212320 let call = if let Some ( arguments) = arguments {
22222321 self . compile_call_inner ( 2 , arguments) ?
22232322 } else {
@@ -2226,13 +2325,8 @@ impl Compiler<'_> {
22262325 self . compile_normal_call ( call) ;
22272326 }
22282327
2229- // Pop the special type params symbol table
2230- if type_params. is_some ( ) {
2231- self . pop_symbol_table ( ) ;
2232- }
2233-
2328+ // Step 4: Apply decorators and store (common to both paths)
22342329 self . apply_decorators ( decorator_list) ;
2235-
22362330 self . store_name ( name)
22372331 }
22382332
0 commit comments