Skip to content

Commit 15b4c0a

Browse files
committed
Bytecode parity
1 parent 535c5e2 commit 15b4c0a

File tree

4 files changed

+26
-46
lines changed

4 files changed

+26
-46
lines changed

crates/codegen/src/compile.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4593,6 +4593,11 @@ impl Compiler {
45934593
if fname == "__init_subclass__" || fname == "__class_getitem__" {
45944594
continue;
45954595
}
4596+
// For __new__, scan for "self" (not the first param "cls")
4597+
if fname == "__new__" {
4598+
Self::scan_store_attrs(&f.body, "self", attrs);
4599+
continue;
4600+
}
45964601
let first_param = f
45974602
.parameters
45984603
.posonlyargs
@@ -4602,13 +4607,7 @@ impl Compiler {
46024607
let Some(self_name) = first_param else {
46034608
continue;
46044609
};
4605-
// For __new__, first param is cls → scan for "self" instead
4606-
// (CPython scans for the literal name "self" in __new__ bodies)
4607-
if fname == "__new__" {
4608-
Self::scan_store_attrs(&f.body, "self", attrs);
4609-
} else {
4610-
Self::scan_store_attrs(&f.body, self_name.as_str(), attrs);
4611-
}
4610+
Self::scan_store_attrs(&f.body, self_name.as_str(), attrs);
46124611
}
46134612
}
46144613

@@ -5469,9 +5468,9 @@ impl Compiler {
54695468

54705469
// The thing iterated:
54715470
// Optimize: `for x in [a, b, c]` → use tuple instead of list
5472-
// (list creation is wasteful for iteration)
5473-
// Skip optimization if any element is starred (e.g., `[a, *b, c]`)
5474-
if let ast::Expr::List(ast::ExprList { elts, .. }) = iter
5471+
// Skip for async-for (GET_AITER expects the original type)
5472+
if !is_async
5473+
&& let ast::Expr::List(ast::ExprList { elts, .. }) = iter
54755474
&& !elts.iter().any(|e| matches!(e, ast::Expr::Starred(_)))
54765475
{
54775476
for elt in elts {
@@ -7609,6 +7608,14 @@ impl Compiler {
76097608
compiler.compile_comprehension_element(elt)?;
76107609

76117610
compiler.mark_generator();
7611+
if compiler.ctx.func == FunctionContext::AsyncFunction {
7612+
emit!(
7613+
compiler,
7614+
Instruction::CallIntrinsic1 {
7615+
func: bytecode::IntrinsicFunction1::AsyncGenWrap
7616+
}
7617+
);
7618+
}
76127619
// arg=0: direct yield (wrapped for async generators)
76137620
emit!(compiler, Instruction::YieldValue { arg: 0 });
76147621
emit!(

crates/codegen/src/ir.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,6 @@ impl CodeInfo {
209209
// Always apply LOAD_FAST_BORROW optimization
210210
self.optimize_load_fast_borrow();
211211

212-
// Add StopIteration handler for generators/coroutines (before label_exception_targets)
213-
add_generator_stop_handler(self.flags, &mut self.blocks);
214-
215212
// Post-codegen CFG analysis passes (flowgraph.c pipeline)
216213
mark_except_handlers(&mut self.blocks);
217214
label_exception_targets(&mut self.blocks);
@@ -2026,17 +2023,6 @@ fn normalize_jumps(blocks: &mut [Block]) {
20262023
/// to the final return block. Matches CPython's behavior of ensuring every
20272024
/// code path that reaches the end of a function/module has its own explicit
20282025
/// return instruction.
2029-
/// Add a StopIteration → RuntimeError handler for generators/coroutines.
2030-
/// CPython: flowgraph.c add_stopiteration_handler()
2031-
/// Appends a handler block that wraps the entire generator body.
2032-
fn add_generator_stop_handler(_flags: CodeFlags, _blocks: &mut Vec<Block>) {
2033-
// TODO: Implement generator StopIteration handler.
2034-
// CPython adds PUSH_EXC_INFO + CALL_INTRINSIC_1(STOPITERATION_ERROR) + RERAISE 1
2035-
// as a handler wrapping the entire generator body.
2036-
// Currently disabled: the handler breaks async comprehension execution paths
2037-
// because it interferes with the exception table ordering.
2038-
}
2039-
20402026
fn duplicate_end_returns(blocks: &mut [Block]) {
20412027
// Walk the block chain to find the last block
20422028
let mut last_block = BlockIdx(0);

crates/vm/src/builtins/code.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,13 +1291,17 @@ impl PyCode {
12911291

12921292
let varnames_len = self.code.varnames.len();
12931293
// Non-parameter cells: cellvars that are NOT also in varnames
1294-
let varnames_set: std::collections::HashSet<String> =
1295-
self.code.varnames.iter().map(|s| s.to_string()).collect();
12961294
let nonparam_cellvars: Vec<_> = self
12971295
.code
12981296
.cellvars
12991297
.iter()
1300-
.filter(|s| !varnames_set.contains(&s.to_string()))
1298+
.filter(|s| {
1299+
let s_str: &str = s.as_ref();
1300+
!self.code.varnames.iter().any(|v| {
1301+
let v_str: &str = v.as_ref();
1302+
v_str == s_str
1303+
})
1304+
})
13011305
.collect();
13021306
let nonparam_len = nonparam_cellvars.len();
13031307

crates/vm/src/frame.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use crate::{
1010
PyBaseException, PyBaseExceptionRef, PyBaseObject, PyCode, PyCoroutine, PyDict, PyDictRef,
1111
PyFloat, PyFrozenSet, PyGenerator, PyInt, PyInterpolation, PyList, PyModule, PyProperty,
1212
PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback, PyType, PyUtf8Str,
13-
asyncgenerator::PyAsyncGenWrappedValue,
1413
builtin_func::PyNativeFunction,
1514
descriptor::{MemberGetter, PyMemberDescriptor, PyMethodDescriptor},
1615
frame::stack_analysis,
@@ -3548,7 +3547,7 @@ impl ExecutingFrame<'_> {
35483547

35493548
Ok(None)
35503549
}
3551-
Instruction::YieldValue { arg: oparg } => {
3550+
Instruction::YieldValue { .. } => {
35523551
debug_assert!(
35533552
self.localsplus
35543553
.stack_as_slice()
@@ -3557,16 +3556,7 @@ impl ExecutingFrame<'_> {
35573556
.all(|sr| !sr.is_borrowed()),
35583557
"borrowed refs on stack at yield point"
35593558
);
3560-
let value = self.pop_value();
3561-
// arg=0: direct yield (wrapped for async generators)
3562-
// arg=1: yield from await/yield-from (NOT wrapped)
3563-
let wrap = oparg.get(arg) == 0;
3564-
let value = if wrap && self.code.flags.contains(bytecode::CodeFlags::COROUTINE) {
3565-
PyAsyncGenWrappedValue(value).into_pyobject(vm)
3566-
} else {
3567-
value
3568-
};
3569-
Ok(Some(ExecutionResult::Yield(value)))
3559+
Ok(Some(ExecutionResult::Yield(self.pop_value())))
35703560
}
35713561
Instruction::Send { .. } => {
35723562
// (receiver, v -- receiver, retval)
@@ -5800,13 +5790,6 @@ impl ExecutingFrame<'_> {
58005790
let offset = (self.lasti() - 1) * 2;
58015791
monitoring::fire_py_yield(vm, self.code, offset, &value)?;
58025792
}
5803-
let oparg = u32::from(arg);
5804-
let wrap = oparg == 0;
5805-
let value = if wrap && self.code.flags.contains(bytecode::CodeFlags::COROUTINE) {
5806-
PyAsyncGenWrappedValue(value).into_pyobject(vm)
5807-
} else {
5808-
value
5809-
};
58105793
Ok(Some(ExecutionResult::Yield(value)))
58115794
}
58125795
Instruction::InstrumentedCall => {

0 commit comments

Comments
 (0)