@@ -75,6 +75,18 @@ pub enum FBlockDatum {
7575 ExceptionName ( String ) ,
7676}
7777
78+ /// Type of super() call optimization detected by can_optimize_super_call()
79+ #[ derive( Debug , Clone ) ]
80+ enum SuperCallType < ' a > {
81+ /// super(class, self) - explicit 2-argument form
82+ TwoArg {
83+ class_arg : & ' a Expr ,
84+ self_arg : & ' a Expr ,
85+ } ,
86+ /// super() - implicit 0-argument form (uses __class__ cell)
87+ ZeroArg ,
88+ }
89+
7890#[ derive( Debug , Clone ) ]
7991pub struct FBlockInfo {
8092 pub fb_type : FBlockType ,
@@ -661,6 +673,153 @@ impl Compiler {
661673 self . symbol_table_stack . pop ( ) . expect ( "compiler bug" )
662674 }
663675
676+ /// Check if a super() call can be optimized
677+ /// Returns Some(SuperCallType) if optimization is possible, None otherwise
678+ fn can_optimize_super_call < ' a > (
679+ & self ,
680+ value : & ' a Expr ,
681+ attr : & str ,
682+ ) -> Option < SuperCallType < ' a > > {
683+ use ruff_python_ast:: * ;
684+
685+ // 1. value must be a Call expression
686+ let Expr :: Call ( ExprCall {
687+ func, arguments, ..
688+ } ) = value
689+ else {
690+ return None ;
691+ } ;
692+
693+ // 2. func must be Name("super")
694+ let Expr :: Name ( ExprName { id, .. } ) = func. as_ref ( ) else {
695+ return None ;
696+ } ;
697+ if id. as_str ( ) != "super" {
698+ return None ;
699+ }
700+
701+ // 3. attr must not be "__class__"
702+ if attr == "__class__" {
703+ return None ;
704+ }
705+
706+ // 4. No keyword arguments
707+ if !arguments. keywords . is_empty ( ) {
708+ return None ;
709+ }
710+
711+ // 5. Must be inside a function (not at module level or class body)
712+ if !self . ctx . in_func ( ) {
713+ return None ;
714+ }
715+
716+ // 6. "super" must be GlobalImplicit (not redefined locally or at module level)
717+ let table = self . current_symbol_table ( ) ;
718+ if let Some ( symbol) = table. lookup ( "super" )
719+ && symbol. scope != SymbolScope :: GlobalImplicit
720+ {
721+ return None ;
722+ }
723+ // Also check top-level scope to detect module-level shadowing.
724+ // Only block if super is actually *bound* at module level (not just used).
725+ if let Some ( top_table) = self . symbol_table_stack . first ( )
726+ && let Some ( sym) = top_table. lookup ( "super" )
727+ && sym. scope != SymbolScope :: GlobalImplicit
728+ {
729+ return None ;
730+ }
731+
732+ // 7. Check argument pattern
733+ let args = & arguments. args ;
734+
735+ // No starred expressions allowed
736+ if args. iter ( ) . any ( |arg| matches ! ( arg, Expr :: Starred ( _) ) ) {
737+ return None ;
738+ }
739+
740+ match args. len ( ) {
741+ 2 => {
742+ // 2-arg: super(class, self)
743+ Some ( SuperCallType :: TwoArg {
744+ class_arg : & args[ 0 ] ,
745+ self_arg : & args[ 1 ] ,
746+ } )
747+ }
748+ 0 => {
749+ // 0-arg: super() - need __class__ cell and first parameter
750+ // Enclosing function should have at least one positional argument
751+ let info = self . code_stack . last ( ) ?;
752+ if info. metadata . argcount == 0 && info. metadata . posonlyargcount == 0 {
753+ return None ;
754+ }
755+
756+ // Check if __class__ is available as a cell/free variable
757+ // The scope must be Free (from enclosing class) or have FREE_CLASS flag
758+ if let Some ( symbol) = table. lookup ( "__class__" ) {
759+ if symbol. scope != SymbolScope :: Free
760+ && !symbol. flags . contains ( SymbolFlags :: FREE_CLASS )
761+ {
762+ return None ;
763+ }
764+ } else {
765+ // __class__ not in symbol table, optimization not possible
766+ return None ;
767+ }
768+
769+ Some ( SuperCallType :: ZeroArg )
770+ }
771+ _ => None , // 1 or 3+ args - not optimizable
772+ }
773+ }
774+
775+ /// Load arguments for super() optimization onto the stack
776+ /// Stack result: [global_super, class, self]
777+ fn load_args_for_super ( & mut self , super_type : & SuperCallType < ' _ > ) -> CompileResult < ( ) > {
778+ // 1. Load global super
779+ self . compile_name ( "super" , NameUsage :: Load ) ?;
780+
781+ match super_type {
782+ SuperCallType :: TwoArg {
783+ class_arg,
784+ self_arg,
785+ } => {
786+ // 2-arg: load provided arguments
787+ self . compile_expression ( class_arg) ?;
788+ self . compile_expression ( self_arg) ?;
789+ }
790+ SuperCallType :: ZeroArg => {
791+ // 0-arg: load __class__ cell and first parameter
792+ // Load __class__ from cell/free variable
793+ let scope = self . get_ref_type ( "__class__" ) . map_err ( |e| self . error ( e) ) ?;
794+ let idx = match scope {
795+ SymbolScope :: Cell => self . get_cell_var_index ( "__class__" ) ?,
796+ SymbolScope :: Free => self . get_free_var_index ( "__class__" ) ?,
797+ _ => {
798+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
799+ "super(): __class__ cell not found" . to_owned ( ) ,
800+ ) ) ) ;
801+ }
802+ } ;
803+ self . emit_arg ( idx, Instruction :: LoadDeref ) ;
804+
805+ // Load first parameter (typically 'self').
806+ // Safety: can_optimize_super_call() ensures argcount > 0, and
807+ // parameters are always added to varnames first (see symboltable.rs).
808+ let first_param = {
809+ let info = self . code_stack . last ( ) . unwrap ( ) ;
810+ info. metadata . varnames . first ( ) . cloned ( )
811+ } ;
812+ let first_param = first_param. ok_or_else ( || {
813+ self . error ( CodegenErrorType :: SyntaxError (
814+ "super(): no arguments and no first parameter" . to_owned ( ) ,
815+ ) )
816+ } ) ?;
817+ self . compile_name ( & first_param, NameUsage :: Load ) ?;
818+ }
819+ }
820+ Ok ( ( ) )
821+ }
822+
664823 /// Check if this is an inlined comprehension context (PEP 709)
665824 /// Currently disabled - always returns false to avoid stack issues
666825 fn is_inlined_comprehension_context ( & self , _comprehension_type : ComprehensionType ) -> bool {
@@ -3357,12 +3516,14 @@ impl Compiler {
33573516 /// Determines if a variable should be CELL or FREE type
33583517 // = get_ref_type
33593518 fn get_ref_type ( & self , name : & str ) -> Result < SymbolScope , CodegenErrorType > {
3519+ let table = self . symbol_table_stack . last ( ) . unwrap ( ) ;
3520+
33603521 // Special handling for __class__ and __classdict__ in class scope
3361- if self . ctx . in_class && ( name == "__class__" || name == "__classdict__" ) {
3522+ // This should only apply when we're actually IN a class body,
3523+ // not when we're in a method nested inside a class.
3524+ if table. typ == CompilerScope :: Class && ( name == "__class__" || name == "__classdict__" ) {
33623525 return Ok ( SymbolScope :: Cell ) ;
33633526 }
3364-
3365- let table = self . symbol_table_stack . last ( ) . unwrap ( ) ;
33663527 match table. lookup ( name) {
33673528 Some ( symbol) => match symbol. scope {
33683529 SymbolScope :: Cell => Ok ( SymbolScope :: Cell ) ,
@@ -5732,9 +5893,28 @@ impl Compiler {
57325893 } ;
57335894 }
57345895 Expr :: Attribute ( ExprAttribute { value, attr, .. } ) => {
5735- self . compile_expression ( value) ?;
5736- let idx = self . name ( attr. as_str ( ) ) ;
5737- emit ! ( self , Instruction :: LoadAttr { idx } ) ;
5896+ // Check for super() attribute access optimization
5897+ if let Some ( super_type) = self . can_optimize_super_call ( value, attr. as_str ( ) ) {
5898+ // super().attr or super(cls, self).attr optimization
5899+ // Stack: [global_super, class, self] → LOAD_SUPER_ATTR → [attr]
5900+ self . load_args_for_super ( & super_type) ?;
5901+ let idx = self . name ( attr. as_str ( ) ) ;
5902+ match super_type {
5903+ SuperCallType :: TwoArg { .. } => {
5904+ // LoadSuperAttr (pseudo) - will be converted to real LoadSuperAttr
5905+ // with flags=0b10 (has_class=true, load_method=false) in ir.rs
5906+ emit ! ( self , Instruction :: LoadSuperAttr { arg: idx } ) ;
5907+ }
5908+ SuperCallType :: ZeroArg => {
5909+ emit ! ( self , Instruction :: LoadZeroSuperAttr { idx } ) ;
5910+ }
5911+ }
5912+ } else {
5913+ // Normal attribute access
5914+ self . compile_expression ( value) ?;
5915+ let idx = self . name ( attr. as_str ( ) ) ;
5916+ emit ! ( self , Instruction :: LoadAttr { idx } ) ;
5917+ }
57385918 }
57395919 Expr :: Compare ( ExprCompare {
57405920 left,
@@ -6159,12 +6339,29 @@ impl Compiler {
61596339 // Method call: obj → LOAD_ATTR_METHOD → [method, self_or_null] → args → CALL
61606340 // Regular call: func → PUSH_NULL → args → CALL
61616341 if let Expr :: Attribute ( ExprAttribute { value, attr, .. } ) = & func {
6162- // Method call: compile object, then LOAD_ATTR_METHOD
6163- // LOAD_ATTR_METHOD pushes [method, self_or_null] on stack
6164- self . compile_expression ( value) ?;
6165- let idx = self . name ( attr. as_str ( ) ) ;
6166- emit ! ( self , Instruction :: LoadAttrMethod { idx } ) ;
6167- self . compile_call_helper ( 0 , args) ?;
6342+ // Check for super() method call optimization
6343+ if let Some ( super_type) = self . can_optimize_super_call ( value, attr. as_str ( ) ) {
6344+ // super().method() or super(cls, self).method() optimization
6345+ // Stack: [global_super, class, self] → LOAD_SUPER_METHOD → [method, self]
6346+ self . load_args_for_super ( & super_type) ?;
6347+ let idx = self . name ( attr. as_str ( ) ) ;
6348+ match super_type {
6349+ SuperCallType :: TwoArg { .. } => {
6350+ emit ! ( self , Instruction :: LoadSuperMethod { idx } ) ;
6351+ }
6352+ SuperCallType :: ZeroArg => {
6353+ emit ! ( self , Instruction :: LoadZeroSuperMethod { idx } ) ;
6354+ }
6355+ }
6356+ self . compile_call_helper ( 0 , args) ?;
6357+ } else {
6358+ // Normal method call: compile object, then LOAD_ATTR_METHOD
6359+ // LOAD_ATTR_METHOD pushes [method, self_or_null] on stack
6360+ self . compile_expression ( value) ?;
6361+ let idx = self . name ( attr. as_str ( ) ) ;
6362+ emit ! ( self , Instruction :: LoadAttrMethod { idx } ) ;
6363+ self . compile_call_helper ( 0 , args) ?;
6364+ }
61686365 } else {
61696366 // Regular call: push func, then NULL for self_or_null slot
61706367 // Stack layout: [func, NULL, args...] - same as method call [func, self, args...]
0 commit comments