Skip to content

Commit 40cab9c

Browse files
dependabot[bot]youknowone
authored andcommitted
Bytecode parity
1 parent 3dae07c commit 40cab9c

File tree

5 files changed

+71
-34
lines changed

5 files changed

+71
-34
lines changed

crates/codegen/src/compile.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4581,11 +4581,21 @@ impl Compiler {
45814581
_ => continue,
45824582
};
45834583
// Skip @staticmethod and @classmethod decorated functions
4584-
let dominated_by_special = f.decorator_list.iter().any(|d| {
4584+
let has_special_decorator = f.decorator_list.iter().any(|d| {
45854585
matches!(&d.expression, ast::Expr::Name(n)
45864586
if n.id.as_str() == "staticmethod" || n.id.as_str() == "classmethod")
45874587
});
4588-
if dominated_by_special {
4588+
if has_special_decorator {
4589+
continue;
4590+
}
4591+
// Skip implicit classmethods (__init_subclass__, __class_getitem__)
4592+
let fname = f.name.as_str();
4593+
if fname == "__init_subclass__" || fname == "__class_getitem__" {
4594+
continue;
4595+
}
4596+
// For __new__, scan for "self" (not the first param "cls")
4597+
if fname == "__new__" {
4598+
Self::scan_store_attrs(&f.body, "self", attrs);
45894599
continue;
45904600
}
45914601
let first_param = f
@@ -5458,8 +5468,7 @@ impl Compiler {
54585468

54595469
// The thing iterated:
54605470
// Optimize: `for x in [a, b, c]` → use tuple instead of list
5461-
// (list creation is wasteful for iteration)
5462-
// Skip optimization if any element is starred (e.g., `[a, *b, c]`)
5471+
// Skip for async-for (GET_AITER expects the original type)
54635472
if !is_async
54645473
&& let ast::Expr::List(ast::ExprList { elts, .. }) = iter
54655474
&& !elts.iter().any(|e| matches!(e, ast::Expr::Starred(_)))
@@ -7369,6 +7378,14 @@ impl Compiler {
73697378
Some(expression) => self.compile_expression(expression)?,
73707379
Option::None => self.emit_load_const(ConstantData::None),
73717380
};
7381+
if self.ctx.func == FunctionContext::AsyncFunction {
7382+
emit!(
7383+
self,
7384+
Instruction::CallIntrinsic1 {
7385+
func: bytecode::IntrinsicFunction1::AsyncGenWrap
7386+
}
7387+
);
7388+
}
73727389
// arg=0: direct yield (wrapped for async generators)
73737390
emit!(self, Instruction::YieldValue { arg: 0 });
73747391
emit!(
@@ -7591,6 +7608,14 @@ impl Compiler {
75917608
compiler.compile_comprehension_element(elt)?;
75927609

75937610
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+
}
75947619
// arg=0: direct yield (wrapped for async generators)
75957620
emit!(compiler, Instruction::YieldValue { arg: 0 });
75967621
emit!(
@@ -7891,6 +7916,22 @@ impl Compiler {
78917916
if let ast::Expr::Starred(ast::ExprStarred { value, .. }) = &arguments.args[0] {
78927917
self.compile_expression(value)?;
78937918
}
7919+
} else if !has_starred {
7920+
for arg in &arguments.args {
7921+
self.compile_expression(arg)?;
7922+
}
7923+
self.set_source_range(call_range);
7924+
let positional_count = additional_positional + nelts.to_u32();
7925+
if positional_count == 0 {
7926+
self.emit_load_const(ConstantData::Tuple { elements: vec![] });
7927+
} else {
7928+
emit!(
7929+
self,
7930+
Instruction::BuildTuple {
7931+
count: positional_count
7932+
}
7933+
);
7934+
}
78947935
} else {
78957936
// Use starunpack_helper to build a list, then convert to tuple
78967937
self.starunpack_helper(
@@ -8236,7 +8277,6 @@ impl Compiler {
82368277

82378278
// Create comprehension function with closure
82388279
self.make_closure(code, bytecode::MakeFunctionFlags::new())?;
8239-
emit!(self, Instruction::PushNull);
82408280

82418281
// Evaluate iterated item:
82428282
self.compile_expression(&generators[0].iter)?;
@@ -8250,7 +8290,7 @@ impl Compiler {
82508290
};
82518291

82528292
// Call just created <listcomp> function:
8253-
emit!(self, Instruction::Call { argc: 1 });
8293+
emit!(self, Instruction::Call { argc: 0 });
82548294
if is_async_list_set_dict_comprehension {
82558295
emit!(self, Instruction::GetAwaitable { r#where: 0 });
82568296
self.emit_load_const(ConstantData::None);

crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vm/src/builtins/code.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,22 +1290,34 @@ impl PyCode {
12901290
let idx = usize::try_from(opcode).map_err(|_| idx_err(vm))?;
12911291

12921292
let varnames_len = self.code.varnames.len();
1293-
let cellvars_len = self.code.cellvars.len();
1293+
// Non-parameter cells: cellvars that are NOT also in varnames
1294+
let nonparam_cellvars: Vec<_> = self
1295+
.code
1296+
.cellvars
1297+
.iter()
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+
})
1305+
.collect();
1306+
let nonparam_len = nonparam_cellvars.len();
12941307

12951308
let name = if idx < varnames_len {
1296-
// Index in varnames
1309+
// Index in varnames (includes parameter cells)
12971310
self.code.varnames.get(idx).ok_or_else(|| idx_err(vm))?
1298-
} else if idx < varnames_len + cellvars_len {
1299-
// Index in cellvars
1300-
self.code
1301-
.cellvars
1311+
} else if idx < varnames_len + nonparam_len {
1312+
// Index in non-parameter cellvars
1313+
*nonparam_cellvars
13021314
.get(idx - varnames_len)
13031315
.ok_or_else(|| idx_err(vm))?
13041316
} else {
13051317
// Index in freevars
13061318
self.code
13071319
.freevars
1308-
.get(idx - varnames_len - cellvars_len)
1320+
.get(idx - varnames_len - nonparam_len)
13091321
.ok_or_else(|| idx_err(vm))?
13101322
};
13111323
Ok(name.to_object())

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 => {

crates/vm/src/protocol/object.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,9 @@ impl PyObject {
298298
) -> PyResult<Either<PyObjectRef, bool>> {
299299
let swapped = op.swapped();
300300
let call_cmp = |obj: &Self, other: &Self, op| {
301-
let cmp = obj.class().slots.richcompare.load().unwrap();
301+
let Some(cmp) = obj.class().slots.richcompare.load() else {
302+
return Ok(PyArithmeticValue::NotImplemented);
303+
};
302304
let r = match cmp(obj, other, op, vm)? {
303305
Either::A(obj) => PyArithmeticValue::from_object(vm, obj).map(Either::A),
304306
Either::B(arithmetic) => arithmetic.map(Either::B),

0 commit comments

Comments
 (0)