55//! <https://github.com/python/cpython/blob/main/Python/compile.c>
66//! <https://github.com/micropython/micropython/blob/master/py/compile.c>
77
8+ // spell-checker:ignore starunpack
9+
810#![ deny( clippy:: cast_possible_truncation) ]
911
1012use crate :: {
@@ -47,8 +49,8 @@ use num_complex::Complex;
4749use num_traits:: { Num , ToPrimitive } ;
4850use ruff_python_ast:: {
4951 Alias , Arguments , BoolOp , CmpOp , Comprehension , ConversionFlag , DebugText , Decorator , DictItem ,
50- ExceptHandler , ExceptHandlerExceptHandler , Expr , ExprAttribute , ExprBoolOp , ExprFString ,
51- ExprList , ExprName , ExprStarred , ExprSubscript , ExprTuple , ExprUnaryOp , FString ,
52+ ExceptHandler , ExceptHandlerExceptHandler , Expr , ExprAttribute , ExprBoolOp , ExprContext ,
53+ ExprFString , ExprList , ExprName , ExprStarred , ExprSubscript , ExprTuple , ExprUnaryOp , FString ,
5254 FStringElement , FStringElements , FStringFlags , FStringPart , Identifier , Int , Keyword ,
5355 MatchCase , ModExpression , ModModule , Operator , Parameters , Pattern , PatternMatchAs ,
5456 PatternMatchClass , PatternMatchOr , PatternMatchSequence , PatternMatchSingleton ,
@@ -372,7 +374,7 @@ impl<'src> Compiler<'src> {
372374 }
373375}
374376
375- /// Type of collection to build in star_unpack_helper
377+ /// Type of collection to build in starunpack_helper
376378#[ derive( Debug , Clone , Copy , PartialEq ) ]
377379enum CollectionType {
378380 Tuple ,
@@ -381,15 +383,120 @@ enum CollectionType {
381383}
382384
383385impl Compiler < ' _ > {
386+ /// Check if the slice is a two-element slice (no step)
387+ // = is_two_element_slice
388+ fn is_two_element_slice ( & self , slice : & Expr ) -> bool {
389+ matches ! ( slice, Expr :: Slice ( s) if s. step. is_none( ) )
390+ }
391+
392+ /// Compile a slice expression
393+ // = compiler_slice
394+ fn compile_slice ( & mut self , slice : & Expr ) -> CompileResult < u32 > {
395+ match slice {
396+ Expr :: Slice ( s) => {
397+ // Compile lower
398+ if let Some ( lower) = & s. lower {
399+ self . compile_expression ( lower) ?;
400+ } else {
401+ self . emit_load_const ( ConstantData :: None ) ;
402+ }
403+
404+ // Compile upper
405+ if let Some ( upper) = & s. upper {
406+ self . compile_expression ( upper) ?;
407+ } else {
408+ self . emit_load_const ( ConstantData :: None ) ;
409+ }
410+
411+ // Compile step if present
412+ if let Some ( step) = & s. step {
413+ self . compile_expression ( step) ?;
414+ Ok ( 3 ) // Three values on stack
415+ } else {
416+ Ok ( 2 ) // Two values on stack
417+ }
418+ }
419+ _ => Err ( self . error ( CodegenErrorType :: SyntaxError (
420+ "compile_slice expects a slice expression" . to_owned ( ) ,
421+ ) ) ) ,
422+ }
423+ }
424+
425+ /// Compile a subscript expression
426+ // = compiler_subscript
427+ fn compile_subscript (
428+ & mut self ,
429+ value : & Expr ,
430+ slice : & Expr ,
431+ ctx : ExprContext ,
432+ ) -> CompileResult < ( ) > {
433+ // In CPython:
434+ // 1. Check subscripter and index for Load context
435+ // 2. VISIT value
436+ // 3. Handle two-element slice specially
437+ // 4. Otherwise VISIT slice and emit appropriate instruction
438+
439+ // For Load context, CPython does some checks (we skip for now)
440+ // if ctx == ExprContext::Load {
441+ // check_subscripter(value);
442+ // check_index(value, slice);
443+ // }
444+
445+ // VISIT(c, expr, e->v.Subscript.value)
446+ self . compile_expression ( value) ?;
447+
448+ // Handle two-element slice (for Load/Store, not Del)
449+ if self . is_two_element_slice ( slice) && !matches ! ( ctx, ExprContext :: Del ) {
450+ let n = self . compile_slice ( slice) ?;
451+ match ctx {
452+ ExprContext :: Load => {
453+ // CPython uses BINARY_SLICE
454+ emit ! ( self , Instruction :: BuildSlice { step: n == 3 } ) ;
455+ emit ! ( self , Instruction :: Subscript ) ;
456+ }
457+ ExprContext :: Store => {
458+ // CPython uses STORE_SLICE
459+ emit ! ( self , Instruction :: BuildSlice { step: n == 3 } ) ;
460+ emit ! ( self , Instruction :: StoreSubscript ) ;
461+ }
462+ _ => unreachable ! ( ) ,
463+ }
464+ } else {
465+ // VISIT(c, expr, e->v.Subscript.slice)
466+ self . compile_subscript_slice ( slice) ?;
467+
468+ // Emit appropriate instruction based on context
469+ match ctx {
470+ ExprContext :: Load => emit ! ( self , Instruction :: Subscript ) ,
471+ ExprContext :: Store => emit ! ( self , Instruction :: StoreSubscript ) ,
472+ ExprContext :: Del => emit ! ( self , Instruction :: DeleteSubscript ) ,
473+ ExprContext :: Invalid => {
474+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
475+ "Invalid expression context" . to_owned ( ) ,
476+ ) ) ) ;
477+ }
478+ }
479+ }
480+
481+ Ok ( ( ) )
482+ }
483+
384484 /// Compile a subscript slice, handling starred expressions in tuples
385- /// Similar to CPython's handling in compiler_subscript()
485+ // = compiler_subscript() - specifically the VISIT(c, expr, slice) part
386486 fn compile_subscript_slice ( & mut self , slice : & Expr ) -> CompileResult < ( ) > {
487+ // In CPython, compiler_subscript just does VISIT(c, expr, e->v.Subscript.slice)
488+ // which calls compiler_visit_expr, which in turn calls compiler_tuple for Tuple_kind
489+ // In Load context, compiler_tuple uses starunpack_helper
490+
387491 match slice {
388- // Tuple with potentially starred elements: C[int, *tuple[str, ...], bool]
389- Expr :: Tuple ( ExprTuple { elts, .. } ) => {
390- self . star_unpack_helper ( elts, 0 , CollectionType :: Tuple ) ?;
391- }
392- // Regular expression (including single starred)
492+ // Special handling for tuple in subscript context
493+ // This matches CPython's compiler_tuple behavior for Load context
494+ Expr :: Tuple ( ExprTuple { elts, ctx, .. } ) if matches ! ( ctx, ExprContext :: Load ) => {
495+ // Use starunpack_helper to handle potential starred expressions
496+ // This is equivalent to CPython's compiler_tuple calling starunpack_helper
497+ self . starunpack_helper ( elts, 0 , CollectionType :: Tuple ) ?;
498+ }
499+ // For all other cases (including Store/Del context tuples), compile normally
393500 _ => {
394501 self . compile_expression ( slice) ?;
395502 }
@@ -405,7 +512,7 @@ impl Compiler<'_> {
405512 /// - collection_type: What type of collection to build (tuple, list, set)
406513 ///
407514 // = starunpack_helper in compile.c
408- fn star_unpack_helper (
515+ fn starunpack_helper (
409516 & mut self ,
410517 elts : & [ Expr ] ,
411518 pushed : u32 ,
@@ -1652,10 +1759,10 @@ impl Compiler<'_> {
16521759 let idx = self . name ( attr. as_str ( ) ) ;
16531760 emit ! ( self , Instruction :: DeleteAttr { idx } ) ;
16541761 }
1655- Expr :: Subscript ( ExprSubscript { value , slice , .. } ) => {
1656- self . compile_expression ( value) ? ;
1657- self . compile_subscript_slice ( slice ) ? ;
1658- emit ! ( self , Instruction :: DeleteSubscript ) ;
1762+ Expr :: Subscript ( ExprSubscript {
1763+ value, slice , ctx , ..
1764+ } ) => {
1765+ self . compile_subscript ( value , slice , ctx . clone ( ) ) ? ;
16591766 }
16601767 Expr :: Tuple ( ExprTuple { elts, .. } ) | Expr :: List ( ExprList { elts, .. } ) => {
16611768 for element in elts {
@@ -4020,10 +4127,10 @@ impl Compiler<'_> {
40204127 fn compile_store ( & mut self , target : & Expr ) -> CompileResult < ( ) > {
40214128 match & target {
40224129 Expr :: Name ( ExprName { id, .. } ) => self . store_name ( id. as_str ( ) ) ?,
4023- Expr :: Subscript ( ExprSubscript { value , slice , .. } ) => {
4024- self . compile_expression ( value) ? ;
4025- self . compile_subscript_slice ( slice ) ? ;
4026- emit ! ( self , Instruction :: StoreSubscript ) ;
4130+ Expr :: Subscript ( ExprSubscript {
4131+ value, slice , ctx , ..
4132+ } ) => {
4133+ self . compile_subscript ( value , slice , ctx . clone ( ) ) ? ;
40274134 }
40284135 Expr :: Attribute ( ExprAttribute { value, attr, .. } ) => {
40294136 self . check_forbidden_name ( attr. as_str ( ) , NameUsage :: Store ) ?;
@@ -4104,7 +4211,14 @@ impl Compiler<'_> {
41044211 self . compile_name ( id, NameUsage :: Load ) ?;
41054212 AugAssignKind :: Name { id }
41064213 }
4107- Expr :: Subscript ( ExprSubscript { value, slice, .. } ) => {
4214+ Expr :: Subscript ( ExprSubscript {
4215+ value,
4216+ slice,
4217+ ctx : _,
4218+ ..
4219+ } ) => {
4220+ // For augmented assignment, we need to load the value first
4221+ // But we can't use compile_subscript directly because we need DUP_TOP2
41084222 self . compile_expression ( value) ?;
41094223 self . compile_subscript_slice ( slice) ?;
41104224 emit ! ( self , Instruction :: Duplicate2 ) ;
@@ -4338,10 +4452,10 @@ impl Compiler<'_> {
43384452 // Perform operation:
43394453 self . compile_op ( op, false ) ;
43404454 }
4341- Expr :: Subscript ( ExprSubscript { value , slice , .. } ) => {
4342- self . compile_expression ( value) ? ;
4343- self . compile_subscript_slice ( slice ) ? ;
4344- emit ! ( self , Instruction :: Subscript ) ;
4455+ Expr :: Subscript ( ExprSubscript {
4456+ value, slice , ctx , ..
4457+ } ) => {
4458+ self . compile_subscript ( value , slice , ctx . clone ( ) ) ? ;
43454459 }
43464460 Expr :: UnaryOp ( ExprUnaryOp { op, operand, .. } ) => {
43474461 self . compile_expression ( operand) ?;
@@ -4372,13 +4486,13 @@ impl Compiler<'_> {
43724486 // self.emit_load_const(compile_constant(value));
43734487 // }
43744488 Expr :: List ( ExprList { elts, .. } ) => {
4375- self . star_unpack_helper ( elts, 0 , CollectionType :: List ) ?;
4489+ self . starunpack_helper ( elts, 0 , CollectionType :: List ) ?;
43764490 }
43774491 Expr :: Tuple ( ExprTuple { elts, .. } ) => {
4378- self . star_unpack_helper ( elts, 0 , CollectionType :: Tuple ) ?;
4492+ self . starunpack_helper ( elts, 0 , CollectionType :: Tuple ) ?;
43794493 }
43804494 Expr :: Set ( ExprSet { elts, .. } ) => {
4381- self . star_unpack_helper ( elts, 0 , CollectionType :: Set ) ?;
4495+ self . starunpack_helper ( elts, 0 , CollectionType :: Set ) ?;
43824496 }
43834497 Expr :: Dict ( ExprDict { items, .. } ) => {
43844498 self . compile_dict ( items) ?;
0 commit comments