@@ -1081,6 +1081,14 @@ impl Compiler {
10811081 ) ,
10821082 } ;
10831083
1084+ // Set CO_NESTED for scopes defined inside another function/class/etc.
1085+ // (i.e., not at module level)
1086+ let flags = if self . code_stack . len ( ) > 1 {
1087+ flags | bytecode:: CodeFlags :: NESTED
1088+ } else {
1089+ flags
1090+ } ;
1091+
10841092 // Get private name from parent scope
10851093 let private = if !self . code_stack . is_empty ( ) {
10861094 self . code_stack . last ( ) . unwrap ( ) . private . clone ( )
@@ -1202,7 +1210,8 @@ impl Compiler {
12021210 // enter_scope sets default values based on scope_type, but push_output
12031211 // allows callers to specify exact values
12041212 if let Some ( info) = self . code_stack . last_mut ( ) {
1205- info. flags = flags;
1213+ // Preserve NESTED flag set by enter_scope
1214+ info. flags = flags | ( info. flags & bytecode:: CodeFlags :: NESTED ) ;
12061215 info. metadata . argcount = arg_count;
12071216 info. metadata . posonlyargcount = posonlyarg_count;
12081217 info. metadata . kwonlyargcount = kwonlyarg_count;
@@ -2179,18 +2188,26 @@ impl Compiler {
21792188 }
21802189 }
21812190 ast:: Stmt :: Expr ( ast:: StmtExpr { value, .. } ) => {
2182- self . compile_expression ( value) ?;
2191+ // Optimize away constant expressions with no side effects.
2192+ // In interactive mode, always compile (to print the result).
2193+ let dominated_by_interactive =
2194+ self . interactive && !self . ctx . in_func ( ) && !self . ctx . in_class ;
2195+ if !dominated_by_interactive && Self :: is_const_expression ( value) {
2196+ // Skip compilation entirely - the expression has no side effects
2197+ } else {
2198+ self . compile_expression ( value) ?;
21832199
2184- if self . interactive && ! self . ctx . in_func ( ) && ! self . ctx . in_class {
2185- emit ! (
2186- self ,
2187- Instruction :: CallIntrinsic1 {
2188- func: bytecode:: IntrinsicFunction1 :: Print
2189- }
2190- ) ;
2191- }
2200+ if dominated_by_interactive {
2201+ emit ! (
2202+ self ,
2203+ Instruction :: CallIntrinsic1 {
2204+ func: bytecode:: IntrinsicFunction1 :: Print
2205+ }
2206+ ) ;
2207+ }
21922208
2193- emit ! ( self , Instruction :: PopTop ) ;
2209+ emit ! ( self , Instruction :: PopTop ) ;
2210+ }
21942211 }
21952212 ast:: Stmt :: Global ( _) | ast:: Stmt :: Nonlocal ( _) => {
21962213 // Handled during symbol table construction.
@@ -3748,19 +3765,23 @@ impl Compiler {
37483765 } ) ;
37493766 self . current_code_info ( ) . flags |= bytecode:: CodeFlags :: HAS_DOCSTRING ;
37503767 }
3751- // If no docstring, don't add None to co_consts
3752- // Note: RETURN_GENERATOR + POP_TOP for async functions is emitted in enter_scope()
3753-
37543768 // Compile body statements
37553769 self . compile_statements ( body) ?;
37563770
3757- // Emit None at end if needed
3771+ // Emit implicit `return None` if the body doesn't end with return.
3772+ // Also ensure None is in co_consts even when not emitting return
3773+ // (matching CPython: functions without explicit constants always
3774+ // have None in co_consts).
37583775 match body. last ( ) {
37593776 Some ( ast:: Stmt :: Return ( _) ) => { }
37603777 _ => {
37613778 self . emit_return_const ( ConstantData :: None ) ;
37623779 }
37633780 }
3781+ // Functions with no other constants should still have None in co_consts
3782+ if self . current_code_info ( ) . metadata . consts . is_empty ( ) {
3783+ self . arg_constant ( ConstantData :: None ) ;
3784+ }
37643785
37653786 // Exit scope and create function object
37663787 let code = self . exit_scope ( ) ;
@@ -6882,6 +6903,19 @@ impl Compiler {
68826903 Ok ( send_block)
68836904 }
68846905
6906+ //github.com/ Returns true if the expression is a constant with no side effects.
6907+ fn is_const_expression ( expr : & ast:: Expr ) -> bool {
6908+ matches ! (
6909+ expr,
6910+ ast:: Expr :: StringLiteral ( _)
6911+ | ast:: Expr :: BytesLiteral ( _)
6912+ | ast:: Expr :: NumberLiteral ( _)
6913+ | ast:: Expr :: BooleanLiteral ( _)
6914+ | ast:: Expr :: NoneLiteral ( _)
6915+ | ast:: Expr :: EllipsisLiteral ( _)
6916+ )
6917+ }
6918+
68856919 fn compile_expression ( & mut self , expression : & ast:: Expr ) -> CompileResult < ( ) > {
68866920 trace ! ( "Compiling {expression:?}" ) ;
68876921 let range = expression. range ( ) ;
0 commit comments