Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(_)))
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)?;
Expand All @@ -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);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 19 additions & 7 deletions crates/vm/src/builtins/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
21 changes: 2 additions & 19 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -3548,7 +3547,7 @@ impl ExecutingFrame<'_> {

Ok(None)
}
Instruction::YieldValue { arg: oparg } => {
Instruction::YieldValue { .. } => {
debug_assert!(
self.localsplus
.stack_as_slice()
Expand All @@ -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)
Expand Down Expand Up @@ -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 => {
Expand Down
4 changes: 3 additions & 1 deletion crates/vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Loading