mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Bytecode parity (#7514)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
e564d4646f
commit
d12f623ade
5 files changed
+71
-34
No files matched your search
@@ -4581,11 +4581,21 @@ impl Compiler {
|
||||
_ => continue,
|
||||
};
|
||||
// Skip @staticmethod and @classmethod decorated functions
|
||||
let dominated_by_special = f.decorator_list.iter().any(|d| {
|
||||
let has_special_decorator = f.decorator_list.iter().any(|d| {
|
||||
matches!(&d.expression, ast::Expr::Name(n)
|
||||
if n.id.as_str() == "staticmethod" || n.id.as_str() == "classmethod")
|
||||
});
|
||||
if dominated_by_special {
|
||||
if has_special_decorator {
|
||||
continue;
|
||||
}
|
||||
// Skip implicit classmethods (__init_subclass__, __class_getitem__)
|
||||
let fname = f.name.as_str();
|
||||
if fname == "__init_subclass__" || fname == "__class_getitem__" {
|
||||
continue;
|
||||
}
|
||||
// For __new__, scan for "self" (not the first param "cls")
|
||||
if fname == "__new__" {
|
||||
Self::scan_store_attrs(&f.body, "self", attrs);
|
||||
continue;
|
||||
}
|
||||
let first_param = f
|
||||
@@ -5458,8 +5468,7 @@ impl Compiler {
|
||||
|
||||
// The thing iterated:
|
||||
// Optimize: `for x in [a, b, c]` → use tuple instead of list
|
||||
// (list creation is wasteful for iteration)
|
||||
// Skip optimization if any element is starred (e.g., `[a, *b, c]`)
|
||||
// Skip for async-for (GET_AITER expects the original type)
|
||||
if !is_async
|
||||
&& let ast::Expr::List(ast::ExprList { elts, .. }) = iter
|
||||
&& !elts.iter().any(|e| matches!(e, ast::Expr::Starred(_)))
|
||||
@@ -7369,6 +7378,14 @@ impl Compiler {
|
||||
Some(expression) => self.compile_expression(expression)?,
|
||||
Option::None => self.emit_load_const(ConstantData::None),
|
||||
};
|
||||
if self.ctx.func == FunctionContext::AsyncFunction {
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic1 {
|
||||
func: bytecode::IntrinsicFunction1::AsyncGenWrap
|
||||
}
|
||||
);
|
||||
}
|
||||
// arg=0: direct yield (wrapped for async generators)
|
||||
emit!(self, Instruction::YieldValue { arg: 0 });
|
||||
emit!(
|
||||
@@ -7591,6 +7608,14 @@ impl Compiler {
|
||||
compiler.compile_comprehension_element(elt)?;
|
||||
|
||||
compiler.mark_generator();
|
||||
if compiler.ctx.func == FunctionContext::AsyncFunction {
|
||||
emit!(
|
||||
compiler,
|
||||
Instruction::CallIntrinsic1 {
|
||||
func: bytecode::IntrinsicFunction1::AsyncGenWrap
|
||||
}
|
||||
);
|
||||
}
|
||||
// arg=0: direct yield (wrapped for async generators)
|
||||
emit!(compiler, Instruction::YieldValue { arg: 0 });
|
||||
emit!(
|
||||
@@ -7891,6 +7916,22 @@ impl Compiler {
|
||||
if let ast::Expr::Starred(ast::ExprStarred { value, .. }) = &arguments.args[0] {
|
||||
self.compile_expression(value)?;
|
||||
}
|
||||
} else if !has_starred {
|
||||
for arg in &arguments.args {
|
||||
self.compile_expression(arg)?;
|
||||
}
|
||||
self.set_source_range(call_range);
|
||||
let positional_count = additional_positional + nelts.to_u32();
|
||||
if positional_count == 0 {
|
||||
self.emit_load_const(ConstantData::Tuple { elements: vec![] });
|
||||
} else {
|
||||
emit!(
|
||||
self,
|
||||
Instruction::BuildTuple {
|
||||
count: positional_count
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Use starunpack_helper to build a list, then convert to tuple
|
||||
self.starunpack_helper(
|
||||
@@ -8236,7 +8277,6 @@ impl Compiler {
|
||||
|
||||
// Create comprehension function with closure
|
||||
self.make_closure(code, bytecode::MakeFunctionFlags::new())?;
|
||||
emit!(self, Instruction::PushNull);
|
||||
|
||||
// Evaluate iterated item:
|
||||
self.compile_expression(&generators[0].iter)?;
|
||||
@@ -8250,7 +8290,7 @@ impl Compiler {
|
||||
};
|
||||
|
||||
// Call just created <listcomp> function:
|
||||
emit!(self, Instruction::Call { argc: 1 });
|
||||
emit!(self, Instruction::Call { argc: 0 });
|
||||
if is_async_list_set_dict_comprehension {
|
||||
emit!(self, Instruction::GetAwaitable { r#where: 0 });
|
||||
self.emit_load_const(ConstantData::None);
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9591
|
||||
assertion_line: 9780
|
||||
expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
|
||||
@@ -1290,22 +1290,34 @@ impl PyCode {
|
||||
let idx = usize::try_from(opcode).map_err(|_| idx_err(vm))?;
|
||||
|
||||
let varnames_len = self.code.varnames.len();
|
||||
let cellvars_len = self.code.cellvars.len();
|
||||
// Non-parameter cells: cellvars that are NOT also in varnames
|
||||
let nonparam_cellvars: Vec<_> = self
|
||||
.code
|
||||
.cellvars
|
||||
.iter()
|
||||
.filter(|s| {
|
||||
let s_str: &str = s.as_ref();
|
||||
!self.code.varnames.iter().any(|v| {
|
||||
let v_str: &str = v.as_ref();
|
||||
v_str == s_str
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let nonparam_len = nonparam_cellvars.len();
|
||||
|
||||
let name = if idx < varnames_len {
|
||||
// Index in varnames
|
||||
// Index in varnames (includes parameter cells)
|
||||
self.code.varnames.get(idx).ok_or_else(|| idx_err(vm))?
|
||||
} else if idx < varnames_len + cellvars_len {
|
||||
// Index in cellvars
|
||||
self.code
|
||||
.cellvars
|
||||
} else if idx < varnames_len + nonparam_len {
|
||||
// Index in non-parameter cellvars
|
||||
*nonparam_cellvars
|
||||
.get(idx - varnames_len)
|
||||
.ok_or_else(|| idx_err(vm))?
|
||||
} else {
|
||||
// Index in freevars
|
||||
self.code
|
||||
.freevars
|
||||
.get(idx - varnames_len - cellvars_len)
|
||||
.get(idx - varnames_len - nonparam_len)
|
||||
.ok_or_else(|| idx_err(vm))?
|
||||
};
|
||||
Ok(name.to_object())
|
||||
|
||||
+2
-19
@@ -10,7 +10,6 @@ use crate::{
|
||||
PyBaseException, PyBaseExceptionRef, PyBaseObject, PyCode, PyCoroutine, PyDict, PyDictRef,
|
||||
PyFloat, PyFrozenSet, PyGenerator, PyInt, PyInterpolation, PyList, PyModule, PyProperty,
|
||||
PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback, PyType, PyUtf8Str,
|
||||
asyncgenerator::PyAsyncGenWrappedValue,
|
||||
builtin_func::PyNativeFunction,
|
||||
descriptor::{MemberGetter, PyMemberDescriptor, PyMethodDescriptor},
|
||||
frame::stack_analysis,
|
||||
@@ -3548,7 +3547,7 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::YieldValue { arg: oparg } => {
|
||||
Instruction::YieldValue { .. } => {
|
||||
debug_assert!(
|
||||
self.localsplus
|
||||
.stack_as_slice()
|
||||
@@ -3557,16 +3556,7 @@ impl ExecutingFrame<'_> {
|
||||
.all(|sr| !sr.is_borrowed()),
|
||||
"borrowed refs on stack at yield point"
|
||||
);
|
||||
let value = self.pop_value();
|
||||
// arg=0: direct yield (wrapped for async generators)
|
||||
// arg=1: yield from await/yield-from (NOT wrapped)
|
||||
let wrap = oparg.get(arg) == 0;
|
||||
let value = if wrap && self.code.flags.contains(bytecode::CodeFlags::COROUTINE) {
|
||||
PyAsyncGenWrappedValue(value).into_pyobject(vm)
|
||||
} else {
|
||||
value
|
||||
};
|
||||
Ok(Some(ExecutionResult::Yield(value)))
|
||||
Ok(Some(ExecutionResult::Yield(self.pop_value())))
|
||||
}
|
||||
Instruction::Send { .. } => {
|
||||
// (receiver, v -- receiver, retval)
|
||||
@@ -5800,13 +5790,6 @@ impl ExecutingFrame<'_> {
|
||||
let offset = (self.lasti() - 1) * 2;
|
||||
monitoring::fire_py_yield(vm, self.code, offset, &value)?;
|
||||
}
|
||||
let oparg = u32::from(arg);
|
||||
let wrap = oparg == 0;
|
||||
let value = if wrap && self.code.flags.contains(bytecode::CodeFlags::COROUTINE) {
|
||||
PyAsyncGenWrappedValue(value).into_pyobject(vm)
|
||||
} else {
|
||||
value
|
||||
};
|
||||
Ok(Some(ExecutionResult::Yield(value)))
|
||||
}
|
||||
Instruction::InstrumentedCall => {
|
||||
|
||||
@@ -298,7 +298,9 @@ impl PyObject {
|
||||
) -> PyResult<Either<PyObjectRef, bool>> {
|
||||
let swapped = op.swapped();
|
||||
let call_cmp = |obj: &Self, other: &Self, op| {
|
||||
let cmp = obj.class().slots.richcompare.load().unwrap();
|
||||
let Some(cmp) = obj.class().slots.richcompare.load() else {
|
||||
return Ok(PyArithmeticValue::NotImplemented);
|
||||
};
|
||||
let r = match cmp(obj, other, op, vm)? {
|
||||
Either::A(obj) => PyArithmeticValue::from_object(vm, obj).map(Either::A),
|
||||
Either::B(arithmetic) => arithmetic.map(Either::B),
|
||||
|
||||
Reference in new issue
Block a user