From bc3432da1dba89a51291ec8107eec9312e48d2e9 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 25 Sep 2026 23:32:25 +0900 Subject: [PATCH 01/12] Read PyTuple payloads through as_slice() - Add Py>::as_slice(); it is generic over R, so typed tuples use the same view. - Callers reach slice methods, indexing and slice arguments through as_slice(). - PyTuple's own sequence and mapping slots and marshal's set_marshal_item reach the remaining PyTuple methods through the payload field. - The Py::payload field is pub(crate). Assisted-by: Claude Code:claude-opus-5-5 --- crates/capi/src/abstract_.rs | 6 +- crates/capi/src/pyerrors.rs | 1 + crates/capi/src/tupleobject.rs | 8 +-- crates/stdlib/src/_asyncio.rs | 14 +++-- crates/stdlib/src/json.rs | 4 +- crates/stdlib/src/pickle.rs | 16 ++--- crates/stdlib/src/socket.rs | 18 +++--- crates/stdlib/src/ssl/error.rs | 2 +- crates/vm/src/builtins/code.rs | 5 ++ crates/vm/src/builtins/dict.rs | 6 +- crates/vm/src/builtins/function.rs | 8 +-- crates/vm/src/builtins/function/jit.rs | 4 +- crates/vm/src/builtins/genericalias.rs | 11 ++-- crates/vm/src/builtins/list.rs | 2 +- crates/vm/src/builtins/memory.rs | 17 +++--- crates/vm/src/builtins/object.rs | 9 ++- crates/vm/src/builtins/template.rs | 23 ++++---- crates/vm/src/builtins/tuple.rs | 30 +++++----- crates/vm/src/builtins/type.rs | 58 +++++++++++-------- crates/vm/src/builtins/union.rs | 23 ++++---- crates/vm/src/cformat.rs | 4 +- crates/vm/src/class.rs | 2 +- crates/vm/src/codecs.rs | 14 ++--- crates/vm/src/exception_group.rs | 28 +++++---- crates/vm/src/exceptions.rs | 32 +++++----- crates/vm/src/frame.rs | 29 ++++++---- crates/vm/src/object/core.rs | 8 +-- crates/vm/src/protocol/object.rs | 4 +- crates/vm/src/protocol/sequence.rs | 2 +- crates/vm/src/py_serde.rs | 2 +- crates/vm/src/stdlib/_abc.rs | 2 +- crates/vm/src/stdlib/_ast/constant.rs | 2 +- .../src/stdlib/_collections/ordered_dict.rs | 13 +++-- crates/vm/src/stdlib/_ctypes.rs | 8 +-- crates/vm/src/stdlib/_ctypes/base.rs | 10 ++-- crates/vm/src/stdlib/_ctypes/function.rs | 5 +- crates/vm/src/stdlib/_ctypes/simple.rs | 2 + crates/vm/src/stdlib/_ctypes/structure.rs | 21 +++---- crates/vm/src/stdlib/_ctypes/union.rs | 21 +++---- crates/vm/src/stdlib/_functools.rs | 15 ++--- crates/vm/src/stdlib/_interpreters.rs | 1 + crates/vm/src/stdlib/_io.rs | 30 +++++----- crates/vm/src/stdlib/_testinternalcapi.rs | 4 +- crates/vm/src/stdlib/_thread.rs | 2 +- crates/vm/src/stdlib/_typing.rs | 8 ++- crates/vm/src/stdlib/_warnings.rs | 2 +- crates/vm/src/stdlib/builtins.rs | 18 +++--- crates/vm/src/stdlib/marshal.rs | 8 +-- crates/vm/src/stdlib/os.rs | 6 +- crates/vm/src/stdlib/posix.rs | 4 +- crates/vm/src/stdlib/time.rs | 2 +- crates/vm/src/stdlib/typevar.rs | 6 +- crates/vm/src/suggestion.rs | 4 +- crates/vm/src/types/structseq.rs | 20 ++++--- crates/vm/src/vm/crossinterp.rs | 14 ++++- crates/vm/src/vm/mod.rs | 4 +- crates/vm/src/warn.rs | 18 +++--- crates/wasm/src/ssl.rs | 2 +- 58 files changed, 358 insertions(+), 284 deletions(-) diff --git a/crates/capi/src/abstract_.rs b/crates/capi/src/abstract_.rs index a1052ad026b..8447d502363 100644 --- a/crates/capi/src/abstract_.rs +++ b/crates/capi/src/abstract_.rs @@ -19,7 +19,7 @@ mod sequence; const PY_VECTORCALL_ARGUMENTS_OFFSET: usize = 1usize << (usize::BITS as usize - 1); fn tuple_to_args(tuple: &Py) -> PosArgs { - tuple.iter().cloned().collect::>().into() + tuple.as_slice().iter().cloned().collect::>().into() } fn dict_to_kwargs(vm: &VirtualMachine, dict: &Py) -> PyResult { @@ -89,7 +89,7 @@ pub unsafe extern "C" fn PyObject_Vectorcall( let kwnames: Option<&[PyObjectRef]> = unsafe { kwnames .assume_borrowed_or_opt() - .map(|tuple| Ok(&***tuple.try_downcast_ref::(vm)?)) + .map(|tuple| Ok(tuple.try_downcast_ref::(vm)?.as_slice())) .transpose()? }; @@ -150,7 +150,7 @@ pub unsafe extern "C" fn PyVectorcall_Call( let callable = unsafe { callable.assume_borrowed() }; let tuple = unsafe { tuple.assume_borrowed_and_cast::(vm) }?; - let mut args = tuple.iter().cloned().collect::>(); + let mut args = tuple.as_slice().iter().cloned().collect::>(); let num_positional_args = args.len(); let mut kwnames = Vec::new(); diff --git a/crates/capi/src/pyerrors.rs b/crates/capi/src/pyerrors.rs index 3422e946f8f..e43358ae348 100644 --- a/crates/capi/src/pyerrors.rs +++ b/crates/capi/src/pyerrors.rs @@ -211,6 +211,7 @@ pub unsafe extern "C" fn PyErr_NewException( vec![ty.to_owned()] } else if let Some(tuple) = bases.downcast_ref::() { tuple + .as_slice() .iter() .map(|item| item.to_owned().downcast()) .collect::, _>>() diff --git a/crates/capi/src/tupleobject.rs b/crates/capi/src/tupleobject.rs index 0c69abb6ea3..5ab871d8e11 100644 --- a/crates/capi/src/tupleobject.rs +++ b/crates/capi/src/tupleobject.rs @@ -57,7 +57,7 @@ pub extern "C" fn PyTuple_SetItem( pub unsafe extern "C" fn PyTuple_Size(tuple: *mut PyObject) -> isize { with_vm(|vm| { let tuple = unsafe { tuple.assume_borrowed_and_cast::(vm) }?; - Ok(tuple.__len__()) + Ok(tuple.as_slice().len()) }) } @@ -68,7 +68,7 @@ pub unsafe extern "C" fn PyTuple_GetItem(tuple: *mut PyObject, pos: isize) -> *m let result: &PyObject = pos .try_into() .ok() - .and_then(|index: usize| tuple.get(index)) + .and_then(|index: usize| tuple.as_slice().get(index)) .ok_or_else(|| vm.new_index_error("tuple index out of range"))?; Ok(result.as_raw()) @@ -83,10 +83,10 @@ pub unsafe extern "C" fn PyTuple_GetSlice( ) -> *mut PyObject { with_vm(|vm| { let tuple = unsafe { tuple.assume_borrowed_and_cast::(vm) }?; - let len = tuple.__len__() as isize; + let len = tuple.as_slice().len() as isize; let low = low.clamp(0, len); let high = high.clamp(low, len); - let slice = tuple.do_slice(low as usize..high as usize); + let slice = tuple.as_slice().do_slice(low as usize..high as usize); Ok(vm.ctx.new_tuple(slice)) }) } diff --git a/crates/stdlib/src/_asyncio.rs b/crates/stdlib/src/_asyncio.rs index ebf52e008a0..9432111d100 100644 --- a/crates/stdlib/src/_asyncio.rs +++ b/crates/stdlib/src/_asyncio.rs @@ -408,7 +408,7 @@ pub(crate) mod _asyncio { let item = list.borrow_vec().first().cloned(); if let Some(item) = item { let tuple: &Py = item.downcast_ref().unwrap(); - let cb = tuple.first().unwrap().clone(); + let cb = tuple.as_slice().first().unwrap().clone(); let cmp = vm.identical_or_equal(&cb, &func)?; if cmp { *self.fut_callbacks.write() = None; @@ -445,7 +445,7 @@ pub(crate) mod _asyncio { }; let tuple: &Py = item.downcast_ref().unwrap(); - let cb = tuple.first().unwrap().clone(); + let cb = tuple.as_slice().first().unwrap().clone(); let cmp = vm.identical_or_equal(&cb, &func)?; if !cmp { @@ -544,7 +544,8 @@ pub(crate) mod _asyncio { let items: Vec<_> = list.borrow_vec().iter().cloned().collect(); for item in items { if let Some(tuple) = item.downcast_ref::() - && let (Some(cb), Some(ctx)) = (tuple.first(), tuple.get(1)) + && let (Some(cb), Some(ctx)) = + (tuple.as_slice().first(), tuple.as_slice().get(1)) { callbacks_to_call.push((cb.clone(), Some(ctx.clone()))); } @@ -1425,7 +1426,7 @@ pub(crate) mod _asyncio { let item = list.borrow_vec().first().cloned(); if let Some(item) = item { let tuple: &Py = item.downcast_ref().unwrap(); - let cb = tuple.first().unwrap().clone(); + let cb = tuple.as_slice().first().unwrap().clone(); let cmp = vm.identical_or_equal(&cb, &func)?; if cmp { *self.base.fut_callbacks.write() = None; @@ -1462,7 +1463,7 @@ pub(crate) mod _asyncio { }; let tuple: &Py = item.downcast_ref().unwrap(); - let cb = tuple.first().unwrap().clone(); + let cb = tuple.as_slice().first().unwrap().clone(); let cmp = vm.identical_or_equal(&cb, &func)?; if !cmp { @@ -1504,7 +1505,8 @@ pub(crate) mod _asyncio { let items: Vec<_> = list.borrow_vec().iter().cloned().collect(); for item in items { if let Some(tuple) = item.downcast_ref::() - && let (Some(cb), Some(ctx)) = (tuple.first(), tuple.get(1)) + && let (Some(cb), Some(ctx)) = + (tuple.as_slice().first(), tuple.as_slice().get(1)) { callbacks_to_call.push((cb.clone(), Some(ctx.clone()))); } diff --git a/crates/stdlib/src/json.rs b/crates/stdlib/src/json.rs index a9e8025684b..7e7e622bff9 100644 --- a/crates/stdlib/src/json.rs +++ b/crates/stdlib/src/json.rs @@ -626,7 +626,7 @@ mod _json { Ok(tuple) => { use crate::vm::builtins::PyTupleRef; let tuple: PyTupleRef = tuple.try_into_value(vm)?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_value_error("scan_once must return 2-tuple")); } let value = tuple.as_slice()[0].clone(); @@ -1144,7 +1144,7 @@ mod _json { vm.extract_elements_with(&items_obj, |item| { use crate::vm::builtins::PyTupleRef; let tuple: PyTupleRef = item.try_into_value(vm)?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_value_error("items() must return 2-tuples")); } let slice = tuple.as_slice(); diff --git a/crates/stdlib/src/pickle.rs b/crates/stdlib/src/pickle.rs index 98775bb9e0a..1b29062e635 100644 --- a/crates/stdlib/src/pickle.rs +++ b/crates/stdlib/src/pickle.rs @@ -683,7 +683,7 @@ mod _pickle { let mapped: PyTupleRef = mapped.downcast().map_err(|_| { vm.new_runtime_error("_compat_pickle.NAME_MAPPING values must be 2-tuples") })?; - if mapped.len() != 2 { + if mapped.as_slice().len() != 2 { return Err( vm.new_runtime_error("_compat_pickle.NAME_MAPPING values must be 2-tuples") ); @@ -1533,7 +1533,9 @@ mod _pickle { return Ok(()); } let (state, slotstate) = match state.downcast_ref::() { - Some(t) if t.len() == 2 => (t.as_slice()[0].clone(), Some(t.as_slice()[1].clone())), + Some(t) if t.as_slice().len() == 2 => { + (t.as_slice()[0].clone(), Some(t.as_slice()[1].clone())) + } _ => (state, None), }; if !vm.is_none(&state) && state.try_to_bool(vm)? { @@ -1592,7 +1594,7 @@ mod _pickle { let key: PyTupleRef = key .downcast() .map_err(|_| vm.new_value_error("_inverted_registry values must be 2-tuples"))?; - if key.len() != 2 { + if key.as_slice().len() != 2 { return Err(vm.new_value_error("_inverted_registry values must be 2-tuples")); } let module = key.as_slice()[0].clone(); @@ -2077,7 +2079,7 @@ mod _pickle { let pair: PyTupleRef = val .downcast() .map_err(|_| vm.new_type_error("'memo' values must be 2-item tuples"))?; - if pair.len() != 2 { + if pair.as_slice().len() != 2 { return Err(vm.new_type_error("'memo' values must be 2-item tuples")); } let idx = pair.as_slice()[0] @@ -2494,7 +2496,7 @@ mod _pickle { fn save_tuple(&mut self, obj: &PyObject, vm: &VirtualMachine) -> PyResult<()> { let tuple = obj.downcast_ref::().expect("tuple"); - let n = tuple.len(); + let n = tuple.as_slice().len(); if n == 0 { if self.bin { self.write(&[EMPTY_TUPLE as u8]); @@ -2750,7 +2752,7 @@ mod _pickle { let pair: PyTupleRef = o.downcast().map_err(|_| { vm.new_type_error("dict items iterator must return 2-tuples") })?; - if pair.len() != 2 { + if pair.as_slice().len() != 2 { return Err( vm.new_type_error("dict items iterator must return 2-tuples") ); @@ -3616,7 +3618,7 @@ mod _pickle { Ok(p) => p, Err(_) => continue, }; - if pair.len() != 2 { + if pair.as_slice().len() != 2 { continue; } let (key, module) = (pair.as_slice()[0].clone(), pair.as_slice()[1].clone()); diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 8c9c27d39e2..9b0c8674222 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -1295,12 +1295,12 @@ mod _socket { obj.class().name() )) })?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm .new_type_error("AF_INET address must be a pair (host, post)") .into()); } - let addr = Address::from_tuple(&tuple, vm)?; + let addr = Address::from_tuple(tuple.as_slice(), vm)?; let mut addr4 = get_addr(vm, addr.host, c::AF_INET)?; match &mut addr4 { SocketAddr::V4(addr4) => { @@ -1318,13 +1318,13 @@ mod _socket { obj.class().name() )) })?; - match tuple.len() { + match tuple.as_slice().len() { 2..=4 => {} _ => return Err(vm.new_type_error( "AF_INET6 address must be a tuple (host, port[, flowinfo[, scopeid]])", ).into()), } - let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&tuple, vm)?; + let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(tuple.as_slice(), vm)?; let mut addr6 = get_addr(vm, addr.host, c::AF_INET6)?; match &mut addr6 { SocketAddr::V6(addr6) => { @@ -2525,10 +2525,10 @@ mod _socket { impl TryFromObject for Address { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { let tuple = PyTupleRef::try_from_object(vm, obj)?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { Err(vm.new_type_error("Address tuple should have only 2 values")) } else { - Self::from_tuple(&tuple, vm) + Self::from_tuple(tuple.as_slice(), vm) } } } @@ -3084,13 +3084,13 @@ mod _socket { flags: i32, vm: &VirtualMachine, ) -> Result<(String, String), IoOrPyException> { - match address.len() { + match address.as_slice().len() { 2..=4 => {} _ => { return Err(vm.new_type_error("illegal sockaddr argument").into()); } } - let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&address, vm)?; + let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(address.as_slice(), vm)?; let hints = host_socket::dns::AddrInfoHints { address: c::AF_UNSPEC, socktype: c::SOCK_DGRAM, @@ -3110,7 +3110,7 @@ mod _socket { } match &mut ainfo.sockaddr { SocketAddr::V4(_) => { - if address.len() != 2 { + if address.as_slice().len() != 2 { return Err(vm.new_os_error("IPv4 sockaddr must be 2 tuple").into()); } } diff --git a/crates/stdlib/src/ssl/error.rs b/crates/stdlib/src/ssl/error.rs index 129b1e940f0..041b22b64cd 100644 --- a/crates/stdlib/src/ssl/error.rs +++ b/crates/stdlib/src/ssl/error.rs @@ -38,7 +38,7 @@ pub(crate) mod ssl_error { // Otherwise return str(args) let args = zelf.args(); - if args.len() == 1 { + if args.as_slice().len() == 1 { args.as_slice()[0].str(vm) } else { args.as_object().str(vm) diff --git a/crates/vm/src/builtins/code.rs b/crates/vm/src/builtins/code.rs index 4ed5ee60d26..5731fcb736e 100644 --- a/crates/vm/src/builtins/code.rs +++ b/crates/vm/src/builtins/code.rs @@ -884,6 +884,7 @@ impl Constructor for PyCode { // Convert names tuple to vector of interned strings let names: Box<[&'static PyStrInterned]> = args .names + .as_slice() .iter() .map(|obj| { let s = obj @@ -896,6 +897,7 @@ impl Constructor for PyCode { let varnames: Box<[&'static PyStrInterned]> = args .varnames + .as_slice() .iter() .map(|obj| { let s = obj @@ -908,6 +910,7 @@ impl Constructor for PyCode { let cellvars: Box<[&'static PyStrInterned]> = args .cellvars + .as_slice() .iter() .map(|obj| { let s = obj @@ -920,6 +923,7 @@ impl Constructor for PyCode { let freevars: Box<[&'static PyStrInterned]> = args .freevars + .as_slice() .iter() .map(|obj| { let s = obj @@ -947,6 +951,7 @@ impl Constructor for PyCode { // Convert constants let constants = args .consts + .as_slice() .iter() .map(|obj| { // Convert PyObject to Literal constant. For now, just wrap it diff --git a/crates/vm/src/builtins/dict.rs b/crates/vm/src/builtins/dict.rs index a6e53d9d7f5..ebd43cab55a 100644 --- a/crates/vm/src/builtins/dict.rs +++ b/crates/vm/src/builtins/dict.rs @@ -1682,16 +1682,16 @@ impl AsSequence for PyDictItems { Some(needle) => needle, None => return Ok(false), }; - if needle.len() != 2 { + if needle.as_slice().len() != 2 { return Ok(false); } let zelf = PyDictItems::sequence_downcast(seq); - let key = &needle[0]; + let key = &needle.as_slice()[0]; if !zelf.dict.__contains__(key, vm)? { return Ok(false); } - let value = &needle[1]; + let value = &needle.as_slice()[1]; let found = zelf.dict().__getitem__(key.to_owned(), vm)?; vm.identical_or_equal(&found, value) }), diff --git a/crates/vm/src/builtins/function.rs b/crates/vm/src/builtins/function.rs index c10cbb7199e..c8a57418558 100644 --- a/crates/vm/src/builtins/function.rs +++ b/crates/vm/src/builtins/function.rs @@ -366,7 +366,7 @@ impl PyFunction { .lock() .0 .as_ref() - .map_or(0, |d| d.len()); + .map_or(0, |d| d.as_slice().len()); let n_required = n_expected_args - n_defaults; let (takes_msg, plural) = if n_defaults > 0 { (format!("from {n_required} to {n_expected_args}"), true) @@ -1002,7 +1002,7 @@ impl PyFunction { #[pygetset(setter)] fn set___code__(&self, code: PyRef, vm: &VirtualMachine) -> PyResult<()> { let n_free = code.freevars.len(); - let n_closure = self.closure.as_ref().map_or(0, |c| c.len()); + let n_closure = self.closure.as_ref().map_or(0, |c| c.as_slice().len()); if n_closure != n_free { return Err(vm.new_value_error(format!( "{}() requires a code object with {} free vars, not {}", @@ -1350,12 +1350,12 @@ impl Constructor for PyFunction { // Handle closure - must be a tuple of cells let closure = if let Some(closure_tuple) = args.closure { // Check that closure length matches code's free variables - if closure_tuple.len() != args.code.freevars.len() { + if closure_tuple.as_slice().len() != args.code.freevars.len() { return Err(vm.new_value_error(format!( "{} requires closure of length {}, not {}", args.code.obj_name, args.code.freevars.len(), - closure_tuple.len() + closure_tuple.as_slice().len() ))); } diff --git a/crates/vm/src/builtins/function/jit.rs b/crates/vm/src/builtins/function/jit.rs index 6700aa11539..90efb1031d2 100644 --- a/crates/vm/src/builtins/function/jit.rs +++ b/crates/vm/src/builtins/function/jit.rs @@ -207,8 +207,8 @@ pub(crate) fn get_jit_args<'a>( // fill in positional defaults if let Some(defaults) = defaults { - for (i, default) in defaults.iter().enumerate() { - let arg_idx = i + arg_count as usize - defaults.len(); + for (i, default) in defaults.as_slice().iter().enumerate() { + let arg_idx = i + arg_count as usize - defaults.as_slice().len(); if !jit_args.is_set(arg_idx) { jit_args.set(arg_idx, get_jit_value(vm, default)?)?; } diff --git a/crates/vm/src/builtins/genericalias.rs b/crates/vm/src/builtins/genericalias.rs index a16bb7710a2..3a9a7f0cc4f 100644 --- a/crates/vm/src/builtins/genericalias.rs +++ b/crates/vm/src/builtins/genericalias.rs @@ -196,10 +196,11 @@ impl PyGenericAlias { let repr_str = format!( "{}[{}]", repr_item(&self.origin, vm)?, - if self.args.is_empty() { + if self.args.as_slice().is_empty() { "()".to_owned() } else { self.args + .as_slice() .iter() .map(|o| repr_arg(o, vm)) .collect::>>()? @@ -357,7 +358,7 @@ fn make_parameters_from_slice(args: &[PyObjectRef], vm: &VirtualMachine) -> PyRe let sub = vm.with_recursion("while computing __parameters__", || { make_parameters_from_slice(&items, vm) })?; - for sub_param in sub.iter() { + for sub_param in sub.as_slice() { if tuple_index(¶meters, sub_param).is_none() { parameters.push(sub_param.clone()); } @@ -396,11 +397,11 @@ fn subs_tvars( .and_then(|sub_params| { PyTupleRef::try_from_object(vm, sub_params) .ok() - .filter(|sub_params| !sub_params.is_empty()) + .filter(|sub_params| !sub_params.as_slice().is_empty()) .map(|sub_params| { let mut sub_args = Vec::new(); - for arg in sub_params.iter() { + for arg in sub_params.as_slice() { if let Some(idx) = tuple_index(params.as_slice(), arg) { let param = ¶ms.as_slice()[idx]; let substituted_arg = &arg_items[idx]; @@ -483,7 +484,7 @@ pub(crate) fn subs_parameters( item: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { - let n_params = parameters.len(); + let n_params = parameters.as_slice().len(); if n_params == 0 { return Err(vm.new_type_error(format!("{} is not a generic class", alias.repr(vm)?))); } diff --git a/crates/vm/src/builtins/list.rs b/crates/vm/src/builtins/list.rs index 81fd2b1bdde..90c7e4f9d36 100644 --- a/crates/vm/src/builtins/list.rs +++ b/crates/vm/src/builtins/list.rs @@ -459,7 +459,7 @@ where { use crate::builtins::PyTuple; if let Some(tuple) = obj.downcast_ref_if_exact::(vm) { - tuple.iter().map(|x| f(x.clone())).collect() + tuple.as_slice().iter().map(|x| f(x.clone())).collect() } else if let Some(list) = obj.downcast_ref_if_exact::(vm) { list.borrow_vec().iter().map(|x| f(x.clone())).collect() } else { diff --git a/crates/vm/src/builtins/memory.rs b/crates/vm/src/builtins/memory.rs index 91965f33154..49bbcc35457 100644 --- a/crates/vm/src/builtins/memory.rs +++ b/crates/vm/src/builtins/memory.rs @@ -385,8 +385,8 @@ impl PyMemoryView { self.format_spec .unpack(&bytes[pos..pos + self.format_spec.size()], vm) .map(|x| { - if x.len() == 1 { - x[0].to_owned() + if x.as_slice().len() == 1 { + x.as_slice()[0].to_owned() } else { x.into() } @@ -863,7 +863,7 @@ impl PyMemoryView { return Ok(zelf.into()); } if let Some(tuple) = needle.downcast_ref::() - && tuple.is_empty() + && tuple.as_slice().is_empty() { return zelf.unpack_single(zelf.desc.offset as usize, vm); } @@ -1170,7 +1170,7 @@ impl Py { if needle.is(&vm.ctx.ellipsis) { return self.pack_single(self.desc.offset as usize, value, vm); } else if let Some(tuple) = needle.downcast_ref::() - && tuple.is_empty() + && tuple.as_slice().is_empty() { return self.pack_single(self.desc.offset as usize, value, vm); } @@ -1249,16 +1249,17 @@ impl TryFromObject for SubscriptNeedle { return Ok(Self::Slice(unsafe { obj.downcast_unchecked::() })); } if let Some(tuple) = obj.downcast_ref::() { - if tuple.iter().all(|x| x.number().is_index()) { + if tuple.as_slice().iter().all(|x| x.number().is_index()) { // ptr_from_tuple: each item is converted where it sits, and the // conversion can run Python that releases the view. let indices = tuple + .as_slice() .iter() .map(|x| x.try_index(vm)?.try_to_primitive::(vm)) .try_collect()?; return Ok(Self::MultiIndex(indices)); } - if tuple.iter().all(|x| x.downcastable::()) { + if tuple.as_slice().iter().all(|x| x.downcastable::()) { return Err( vm.new_not_implemented_error("multi-dimensional slicing is not implemented") ); @@ -1649,8 +1650,8 @@ fn format_unpack( vm: &VirtualMachine, ) -> PyResult { format_spec.unpack(bytes, vm).map(|x| { - if x.len() == 1 { - x[0].to_owned() + if x.as_slice().len() == 1 { + x.as_slice()[0].to_owned() } else { x.into() } diff --git a/crates/vm/src/builtins/object.rs b/crates/vm/src/builtins/object.rs index d1dfeeca710..7f33c23b9c8 100644 --- a/crates/vm/src/builtins/object.rs +++ b/crates/vm/src/builtins/object.rs @@ -230,7 +230,10 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine) let has_weakref = if let Some(ext) = obj.class().heaptype_ext() { match &ext.slots { None => true, // Heap type without __slots__ has automatic weakref - Some(slots) => slots.iter().any(|s| s.as_bytes() == b"__weakref__"), + Some(slots) => slots + .as_slice() + .iter() + .any(|s| s.as_bytes() == b"__weakref__"), } } else { let weakref_name = vm.ctx.intern_str("__weakref__"); @@ -623,10 +626,10 @@ fn get_new_arguments( )) })?; - if newargs_tuple.len() != 2 { + if newargs_tuple.as_slice().len() != 2 { return Err(vm.new_value_error(format!( "__getnewargs_ex__ should return a tuple of length 2, not {}", - newargs_tuple.len() + newargs_tuple.as_slice().len() ))); } diff --git a/crates/vm/src/builtins/template.rs b/crates/vm/src/builtins/template.rs index 6fcded56647..471fa1edb07 100644 --- a/crates/vm/src/builtins/template.rs +++ b/crates/vm/src/builtins/template.rs @@ -112,6 +112,7 @@ impl PyTemplate { fn values(&self, vm: &VirtualMachine) -> PyTupleRef { let values: Vec = self .interpolations + .as_slice() .iter() .map(|interp| { interp @@ -136,13 +137,13 @@ impl PyTemplate { let mut new_interps: Vec = Vec::new(); // Add all strings from self except the last one - let self_strings_len = self.strings.len(); + let self_strings_len = self.strings.as_slice().len(); for i in 0..self_strings_len.saturating_sub(1) { - new_strings.push(self.strings.get(i).unwrap().clone()); + new_strings.push(self.strings.as_slice().get(i).unwrap().clone()); } // Add all interpolations from self - for interp in self.interpolations.iter() { + for interp in self.interpolations.as_slice() { new_interps.push(interp.clone()); } @@ -150,6 +151,7 @@ impl PyTemplate { let mut buf = Wtf8Buf::new(); if let Some(s) = self .strings + .as_slice() .get(self_strings_len.saturating_sub(1)) .and_then(|s| s.downcast_ref::()) { @@ -157,6 +159,7 @@ impl PyTemplate { } if let Some(s) = other .strings + .as_slice() .first() .and_then(|s| s.downcast_ref::()) { @@ -165,12 +168,12 @@ impl PyTemplate { new_strings.push(vm.ctx.new_str(buf).into()); // Add remaining strings from other (skip first) - for i in 1..other.strings.len() { - new_strings.push(other.strings.get(i).unwrap().clone()); + for i in 1..other.strings.as_slice().len() { + new_strings.push(other.strings.as_slice().get(i).unwrap().clone()); } // Add all interpolations from other - for interp in other.interpolations.iter() { + for interp in other.interpolations.as_slice() { new_interps.push(interp.clone()); } @@ -308,8 +311,8 @@ impl IterNext for PyTemplateIter { let index = zelf.index.load(Ordering::SeqCst); if from_strings { - if index < zelf.template.strings.len() { - let item = zelf.template.strings.get(index).unwrap(); + if index < zelf.template.strings.as_slice().len() { + let item = zelf.template.strings.as_slice().get(index).unwrap(); zelf.from_strings.store(false, Ordering::SeqCst); // Skip empty strings @@ -321,8 +324,8 @@ impl IterNext for PyTemplateIter { return Ok(PyIterReturn::Return(item.clone())); } return Ok(PyIterReturn::StopIteration(None)); - } else if index < zelf.template.interpolations.len() { - let item = zelf.template.interpolations.get(index).unwrap(); + } else if index < zelf.template.interpolations.as_slice().len() { + let item = zelf.template.interpolations.as_slice().get(index).unwrap(); zelf.index.fetch_add(1, Ordering::SeqCst); zelf.from_strings.store(true, Ordering::SeqCst); return Ok(PyIterReturn::Return(item.clone())); diff --git a/crates/vm/src/builtins/tuple.rs b/crates/vm/src/builtins/tuple.rs index 0f3c763e7bc..f44920ce6b6 100644 --- a/crates/vm/src/builtins/tuple.rs +++ b/crates/vm/src/builtins/tuple.rs @@ -320,7 +320,7 @@ impl<'a, R> core::iter::IntoIterator for &'a Py> { type IntoIter = core::slice::Iter<'a, R>; fn into_iter(self) -> Self::IntoIter { - PyTuple::as_slice(self).iter() + self.as_slice().iter() } } @@ -375,7 +375,7 @@ impl PyTuple { // This only works for `tuple` itself, not its subclasses. zelf } else { - let v = zelf.elements.mul(vm, value)?; + let v = zelf.as_slice().mul(vm, value)?; let elements = v.into_boxed_slice(); Self { elements: TupleElements::new(elements), @@ -385,12 +385,14 @@ impl PyTuple { } } -impl Py { +impl Py> { #[inline] - pub fn as_slice(&self) -> &[PyObjectRef] { - self.payload().as_slice() + pub fn as_slice(&self) -> &[R] { + self.payload.as_slice() } +} +impl Py { pub fn extract_tuple<'a, T: FromPyTuple<'a>>(&'a self, vm: &VirtualMachine) -> PyResult { T::from_pytuple(self, vm) } @@ -535,7 +537,7 @@ impl PyTuple { let tup_arg = if zelf.class().is(vm.ctx.types.tuple_type) { zelf } else { - Self::new_ref(zelf.elements.as_slice().to_vec(), &vm.ctx) + Self::new_ref(zelf.as_slice().to_vec(), &vm.ctx) }; (tup_arg,) } @@ -556,9 +558,9 @@ impl AsMapping for PyTuple { length: atomic_func!(|mapping, _vm| { Ok(PyTuple::mapping_downcast(mapping).as_slice().len()) }), - subscript: atomic_func!( - |mapping, needle, vm| PyTuple::mapping_downcast(mapping)._getitem(needle, vm) - ), + subscript: atomic_func!(|mapping, needle, vm| PyTuple::mapping_downcast(mapping) + .payload + ._getitem(needle, vm)), ..PyMappingMethods::NOT_IMPLEMENTED }); &AS_MAPPING @@ -568,7 +570,7 @@ impl AsMapping for PyTuple { impl AsSequence for PyTuple { fn as_sequence() -> &'static PySequenceMethods { static AS_SEQUENCE: LazyLock = LazyLock::new(|| PySequenceMethods { - length: atomic_func!(|seq, _vm| Ok(PyTuple::sequence_downcast(seq).__len__())), + length: atomic_func!(|seq, _vm| Ok(PyTuple::sequence_downcast(seq).as_slice().len())), concat: atomic_func!(|seq, other, vm| { let zelf = PyTuple::sequence_downcast(seq); match PyTuple::__add__(zelf.to_owned(), other.to_owned(), vm) { @@ -585,11 +587,11 @@ impl AsSequence for PyTuple { }), item: atomic_func!(|seq, i, vm| { let zelf = PyTuple::sequence_downcast(seq); - zelf.elements.getitem_by_index(vm, i) + zelf.as_slice().getitem_by_index(vm, i) }), contains: atomic_func!(|seq, needle, vm| { let zelf = PyTuple::sequence_downcast(seq); - zelf._contains(needle, vm) + zelf.payload._contains(needle, vm) }), ..PySequenceMethods::NOT_IMPLEMENTED }); @@ -755,7 +757,7 @@ impl PyTupleIterator { pub(crate) fn fast_next(&self) -> Option { locked_next(&self.internal, |tuple, pos| { Ok(PyIterReturn::from_result( - tuple.get(pos).cloned().ok_or(None), + tuple.as_slice().get(pos).cloned().ok_or(None), )) }) .ok() @@ -771,7 +773,7 @@ impl IterNext for PyTupleIterator { fn next(zelf: &Py, _vm: &VirtualMachine) -> PyResult { locked_next(&zelf.internal, |tuple, pos| { Ok(PyIterReturn::from_result( - tuple.get(pos).cloned().ok_or(None), + tuple.as_slice().get(pos).cloned().ok_or(None), )) }) } diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index df49d951044..e7f4ee3f3c2 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -668,7 +668,7 @@ impl PyType { } // Assign versions to all direct bases first (MRO invariant). - for base in self.bases.read().iter() { + for base in self.bases.read().as_slice() { if base.assign_version_tag_inner() == 0 { return 0; } @@ -996,18 +996,20 @@ impl PyType { // Leave tp_mro unset so a custom metaclass mro() sees __mro__ is None. Vec::new() } else { - Self::resolve_mro(&bases)? + Self::resolve_mro(bases.as_slice())? }; // Layout flags come from each direct base's own flags. A custom // mro() can omit a physical base from the stored MRO. if bases + .as_slice() .iter() .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_DICT)) { slots.flags |= PyTypeFlags::HAS_DICT; } if bases + .as_slice() .iter() .any(|b| b.slots.flags.has_feature(PyTypeFlags::MANAGED_DICT)) { @@ -1015,6 +1017,7 @@ impl PyType { } if bases + .as_slice() .iter() .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF)) { @@ -1022,10 +1025,10 @@ impl PyType { } // Inherit SEQUENCE and MAPPING flags from base classes - Self::inherit_patma_flags(&mut slots, &bases); + Self::inherit_patma_flags(&mut slots, bases.as_slice()); // Check for __abc_tpflags__ from ABCMeta (for collections.abc.Sequence, Mapping, etc.) - Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx)?; + Self::check_abc_tpflags(&mut slots, &attrs, bases.as_slice(), ctx)?; if slots.basicsize == 0 { slots.basicsize = base.slots.basicsize; @@ -1045,7 +1048,7 @@ impl PyType { )); } - let inherited_abc_tpflags = Self::inherited_abc_tpflags(&bases); + let inherited_abc_tpflags = Self::inherited_abc_tpflags(bases.as_slice()); let new_type = PyRef::new_ref( Self { base: Some(base).into(), @@ -1067,7 +1070,7 @@ impl PyType { } let weakref_type = super::PyWeak::static_type(); - for base in new_type.bases.read().iter() { + for base in new_type.bases.read().as_slice() { base.subclasses.write().push( new_type .as_object() @@ -1146,7 +1149,7 @@ impl PyType { Self::set_alloc(&new_type.slots, new_type.base.deref()); let weakref_type = super::PyWeak::static_type(); - for base in new_type.bases.read().iter() { + for base in new_type.bases.read().as_slice() { base.subclasses.write().push( new_type .as_object() @@ -1748,13 +1751,13 @@ impl PyType { zelf.name() ))); } - if bases_tuple.is_empty() { + if bases_tuple.as_slice().is_empty() { return Err(vm.new_type_error(format!( "can only assign non-empty tuple to {}.__bases__, not ()", zelf.name() ))); } - for base in bases_tuple.iter() { + for base in bases_tuple.as_slice() { if base.downcast_ref::().is_none() { return Err(vm.new_type_error(format!( "{}.__bases__ must be tuple of classes, not '{}'", @@ -1768,7 +1771,7 @@ impl PyType { // Compute the new solid base before committing anything. This also // validates the new bases (BASETYPE flag, no instance layout // conflict), the same checks type creation performs. - let new_base = best_base(&bases, vm)?.to_owned(); + let new_base = best_base(bases.as_slice(), vm)?.to_owned(); // Reject reparenting onto a base whose instances have an incompatible // object layout. @@ -1829,7 +1832,7 @@ impl PyType { }; let result = Self::with_type_lock(vm, || { - for base in bases.iter() { + for base in bases.as_slice() { if is_subtype_with_mro(&base.mro.read(), base, zelf) || (!base.mro.read().is_empty() && type_is_subtype_base_chain(base, zelf, vm)) { @@ -1903,8 +1906,8 @@ impl PyType { // Take no action if tp_bases was replaced through reentrance. if core::ptr::eq(&**zelf.bases.read(), &*bases) { - remove_as_subclass(&old_bases, &mut retired); - add_as_subclass(&bases); + remove_as_subclass(old_bases.as_slice(), &mut retired); + add_as_subclass(bases.as_slice()); zelf.update_all_slots(&vm.ctx); } @@ -2471,12 +2474,12 @@ impl Constructor for PyType { } let name = name.try_into_utf8(vm)?; - let (metatype, base, bases, base_is_type) = if bases.is_empty() { + let (metatype, base, bases, base_is_type) = if bases.as_slice().is_empty() { let base = vm.ctx.types.object_type.to_owned(); let bases = PyTuple::new_ref_typed(vec![base.clone()], &vm.ctx); (metatype, base, bases, false) } else { - for obj in bases.iter() { + for obj in bases.as_slice() { if obj.downcast_ref::().is_none() { if vm .get_attribute_opt(obj, identifier!(vm, __mro_entries__))? @@ -2493,7 +2496,7 @@ impl Constructor for PyType { let bases = bases.try_into_typed::(vm)?; // Search the bases for the proper metatype to deal with this: - let winner = calculate_meta_class(metatype.clone(), &bases, vm)?; + let winner = calculate_meta_class(metatype.clone(), bases.as_slice(), vm)?; let metatype = if !winner.is(&metatype) { if let Some(ref slot_new) = winner.slots.new.load() { // Pass it to the winner @@ -2504,7 +2507,7 @@ impl Constructor for PyType { metatype }; - let base = best_base(&bases, vm)?; + let base = best_base(bases.as_slice(), vm)?; let base_is_type = base.is(vm.ctx.types.type_type); (metatype, base.to_owned(), bases, base_is_type) @@ -2607,6 +2610,7 @@ impl Constructor for PyType { // Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots // But types like weakref.ref have itemsize = 0 and DO allow slots let has_custom_slots = slots + .as_slice() .iter() .any(|s| !matches!(s.as_bytes(), b"__dict__" | b"__weakref__")); if has_custom_slots && base.slots.itemsize > 0 { @@ -2619,7 +2623,7 @@ impl Constructor for PyType { // Validate slot names and track duplicates let mut seen_dict = false; let mut seen_weakref = false; - for slot in slots.iter() { + for slot in slots.as_slice() { // Use isidentifier for validation (handles Unicode properly) if !slot.isidentifier() { return Err(vm.new_type_error("__slots__ must be identifiers")); @@ -2674,13 +2678,14 @@ impl Constructor for PyType { // Check if __dict__ or __weakref__ is in slots let dict_name = "__dict__"; let weakref_name = "__weakref__"; - let has_dict = slots.iter().any(|s| s.as_wtf8() == dict_name); + let has_dict = slots.as_slice().iter().any(|s| s.as_wtf8() == dict_name); let add_weakref = seen_weakref; // Filter out __dict__ and __weakref__ from slots // (they become descriptors, not member slots), then sort so // __class__ assignment can compare layouts by slot name. let mut filtered: Vec = slots + .as_slice() .iter() .filter(|s| s.as_wtf8() != dict_name && s.as_wtf8() != weakref_name) .cloned() @@ -2695,11 +2700,12 @@ impl Constructor for PyType { // FIXME: this is a temporary fix. multi bases with multiple slots will break object let base_member_count = bases + .as_slice() .iter() .map(|base| base.slots.member_count) .max() .unwrap(); - let heaptype_member_count = heaptype_slots.as_ref().map_or(0, |x| x.len()); + let heaptype_member_count = heaptype_slots.as_ref().map_or(0, |x| x.as_slice().len()); let member_count: usize = base_member_count + heaptype_member_count; let mut flags = PyTypeFlags::heap_type_flags(); @@ -3622,7 +3628,7 @@ fn type_is_subtype_base_chain(a: &Py, b: &Py, vm: &VirtualMachin } fn mro_implementation(typ: &Py, vm: &VirtualMachine) -> PyResult> { - for base in typ.bases.read().iter() { + for base in typ.bases.read().as_slice() { if base.mro.read().is_empty() { return Err(vm.new_type_error(format!( "Cannot extend an incomplete type '{}'", @@ -3630,7 +3636,8 @@ fn mro_implementation(typ: &Py, vm: &VirtualMachine) -> PyResult, b: &Py) -> bool { b.heaptype_ext.as_ref().and_then(|e| e.slots.as_ref()), ) { (Some(x), Some(y)) => { - x.len() == y.len() - && x.iter() - .zip(y.iter()) + x.as_slice().len() == y.as_slice().len() + && x.as_slice() + .iter() + .zip(y.as_slice().iter()) .all(|(p, q)| p.as_wtf8() == q.as_wtf8()) } _ => true, diff --git a/crates/vm/src/builtins/union.rs b/crates/vm/src/builtins/union.rs index 6e4be9721d0..137dbfb4ec7 100644 --- a/crates/vm/src/builtins/union.rs +++ b/crates/vm/src/builtins/union.rs @@ -91,6 +91,7 @@ impl PyUnion { Ok(self .args + .as_slice() .iter() .map(|o| repr_item(o, vm)) .collect::>>()? @@ -136,6 +137,7 @@ impl PyUnion { ) -> PyResult { if zelf .args + .as_slice() .iter() .any(|x| x.class().is(vm.ctx.types.generic_alias_type)) { @@ -153,6 +155,7 @@ impl PyUnion { ) -> PyResult { if zelf .args + .as_slice() .iter() .any(|x| x.class().is(vm.ctx.types.generic_alias_type)) { @@ -185,7 +188,7 @@ impl PyUnion { }; // Check for empty union - if args_tuple.is_empty() { + if args_tuple.as_slice().is_empty() { return Err(vm.new_type_error("Cannot create empty Union")); } @@ -243,7 +246,7 @@ fn flatten_args(args: &Py, vm: &VirtualMachine) -> PyTupleRef { let mut total_args = 0; for arg in args { if let Some(pyref) = arg.downcast_ref::() { - total_args += pyref.args.len(); + total_args += pyref.args.as_slice().len(); } else { total_args += 1; }; @@ -252,7 +255,7 @@ fn flatten_args(args: &Py, vm: &VirtualMachine) -> PyTupleRef { let mut flattened_args = Vec::with_capacity(total_args); for arg in args { if let Some(pyref) = arg.downcast_ref::() { - flattened_args.extend(pyref.args.iter().cloned()); + flattened_args.extend(pyref.args.as_slice().iter().cloned()); } else if vm.is_none(arg) { flattened_args.push(vm.ctx.types.none_type.to_owned().into()); } else if arg.downcast_ref::().is_some() { @@ -296,7 +299,7 @@ fn dedup_and_flatten_args(args: &Py, vm: &VirtualMachine) -> PyResult = Vec::with_capacity(args.len()); + let mut new_args: Vec = Vec::with_capacity(args.as_slice().len()); // Track hashable elements using a Python set (uses hash + equality) let hashable_set = PySet::default().into_ref(&vm.ctx); @@ -364,7 +367,7 @@ fn dedup_and_flatten_args(args: &Py, vm: &VirtualMachine) -> PyResult, vm: &VirtualMachine) -> PyResult { let result = dedup_and_flatten_args(args, vm)?; - Ok(match result.args.len() { + Ok(match result.args.as_slice().len() { 1 => result.args.as_slice()[0].to_owned(), _ => PyUnion::from_components(result, vm)?.to_pyobject(vm), }) @@ -380,11 +383,11 @@ impl PyUnion { vm, )?; - Ok(if new_args.is_empty() { + Ok(if new_args.as_slice().is_empty() { make_union(&new_args, vm)? } else { let mut tmp = new_args.as_slice()[0].to_owned(); - for arg in new_args.iter().skip(1) { + for arg in new_args.as_slice().iter().skip(1) { tmp = vm._or(&tmp, arg)?; } tmp @@ -426,7 +429,7 @@ impl Comparable for PyUnion { let other = class_or_notimplemented!(Self, other); // Check if lengths are equal - if zelf.args.len() != other.args.len() { + if zelf.args.as_slice().len() != other.args.as_slice().len() { return Ok(PyComparisonValue::Implemented(false)); } @@ -489,9 +492,9 @@ impl Hashable for PyUnion { fn hash(zelf: &Py, vm: &VirtualMachine) -> PyResult { // If there are any unhashable args from creation time, the union is unhashable if let Some(ref unhashable_args) = zelf.unhashable_args { - let n = unhashable_args.len(); + let n = unhashable_args.as_slice().len(); // Try to hash each previously unhashable arg to get an error - for arg in unhashable_args.iter() { + for arg in unhashable_args.as_slice() { arg.hash(vm)?; } // All previously unhashable args somehow became hashable diff --git a/crates/vm/src/cformat.rs b/crates/vm/src/cformat.rs index a88eba9174b..4cdb98e5970 100644 --- a/crates/vm/src/cformat.rs +++ b/crates/vm/src/cformat.rs @@ -381,7 +381,7 @@ pub(crate) fn cformat_bytes( if !is_mapping && values_obj .downcast_ref::() - .is_none_or(|e| !e.is_empty()) + .is_none_or(|e| !e.as_slice().is_empty()) { return Err(vm.new_type_error("not all arguments converted during bytes formatting")); } @@ -482,7 +482,7 @@ pub(crate) fn cformat_string( if !is_mapping && values_obj .downcast_ref::() - .is_none_or(|e| !e.is_empty()) + .is_none_or(|e| !e.as_slice().is_empty()) { return Err(vm.new_type_error("not all arguments converted during string formatting")); } diff --git a/crates/vm/src/class.rs b/crates/vm/src/class.rs index 24548c1630e..076e97c3b6f 100644 --- a/crates/vm/src/class.rs +++ b/crates/vm/src/class.rs @@ -285,7 +285,7 @@ pub trait PyClassImpl: PyClassDef { add_operators::(class, ctx); // Inherit slots from base types after slots are fully initialized - for base in class.bases.read().iter() { + for base in class.bases.read().as_slice() { class.inherit_slots(base); } diff --git a/crates/vm/src/codecs.rs b/crates/vm/src/codecs.rs index db5efed1806..0be77b8f575 100644 --- a/crates/vm/src/codecs.rs +++ b/crates/vm/src/codecs.rs @@ -44,7 +44,7 @@ pub struct PyCodec(PyTupleRef); impl PyCodec { #[inline] pub fn from_tuple(tuple: PyTupleRef) -> Result { - if tuple.len() == 4 { + if tuple.as_slice().len() == 4 { Ok(Self(tuple)) } else { Err(tuple) @@ -63,12 +63,12 @@ impl PyCodec { #[inline] pub fn get_encode_func(&self) -> &PyObject { - &self.0[0] + &self.0.as_slice()[0] } #[inline] pub fn get_decode_func(&self) -> &PyObject { - &self.0[1] + &self.0.as_slice()[1] } pub fn is_text_codec(&self, vm: &VirtualMachine) -> PyResult { @@ -90,10 +90,10 @@ impl PyCodec { let res = res .downcast::() .ok() - .filter(|tuple| tuple.len() == 2) + .filter(|tuple| tuple.as_slice().len() == 2) .ok_or_else(|| vm.new_type_error("encoder must return a tuple (object, integer)"))?; // we don't actually care about the integer - Ok(res[0].clone()) + Ok(res.as_slice()[0].clone()) } pub fn decode( @@ -110,10 +110,10 @@ impl PyCodec { let res = res .downcast::() .ok() - .filter(|tuple| tuple.len() == 2) + .filter(|tuple| tuple.as_slice().len() == 2) .ok_or_else(|| vm.new_type_error("decoder must return a tuple (object,integer)"))?; // we don't actually care about the integer - Ok(res[0].clone()) + Ok(res.as_slice()[0].clone()) } pub fn get_incremental_encoder( diff --git a/crates/vm/src/exception_group.rs b/crates/vm/src/exception_group.rs index 795490dff93..5232cbe9801 100644 --- a/crates/vm/src/exception_group.rs +++ b/crates/vm/src/exception_group.rs @@ -190,10 +190,12 @@ pub(super) mod types { })?; let result_tuple: PyTupleRef = result.try_into_value(vm)?; let match_part = result_tuple + .as_slice() .first() .cloned() .unwrap_or_else(|| vm.ctx.none()); let rest_part = result_tuple + .as_slice() .get(1) .cloned() .unwrap_or_else(|| vm.ctx.none()); @@ -229,7 +231,10 @@ pub(super) mod types { #[pymethod] fn __str__(zelf: &Py, vm: &VirtualMachine) -> PyResult { let message = zelf.msg.str(vm)?; - let num_excs = zelf.excs.downcast_ref::().map_or(0, |t| t.len()); + let num_excs = zelf + .excs + .downcast_ref::() + .map_or(0, |t| t.as_slice().len()); let suffix = if num_excs == 1 { "" } else { "s" }; let mut result = message.as_wtf8().to_owned(); @@ -251,16 +256,17 @@ pub(super) mod types { .map_err(|_| vm.new_type_error("__repr__ returned non-string"))? } else { let args = zelf.base.args(); - let exceptions_obj = - if args.len() == 2 && args[1].downcast_ref::().is_some() { - let list = match zelf.excs.downcast_ref::() { - Some(tuple) => vm.ctx.new_list(tuple.to_vec()), - None => vm.ctx.new_list(vec![]), - }; - list.into() - } else { - zelf.excs.to_owned() + let exceptions_obj = if args.as_slice().len() == 2 + && args.as_slice()[1].downcast_ref::().is_some() + { + let list = match zelf.excs.downcast_ref::() { + Some(tuple) => vm.ctx.new_list(tuple.as_slice().to_vec()), + None => vm.ctx.new_list(vec![]), }; + list.into() + } else { + zelf.excs.to_owned() + }; exceptions_obj.repr(vm)? }; @@ -411,7 +417,7 @@ pub(super) mod types { .excs .downcast_ref::() .ok_or_else(|| vm.new_type_error("exceptions must be a tuple"))?; - Ok(tuple.to_vec()) + Ok(tuple.as_slice().to_vec()) } enum ConditionMatcher { diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index f01f8b249c9..c692ca21e4d 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -354,7 +354,7 @@ impl VirtualMachine { fn exception_args_as_string(&self, varargs: &Py, str_single: bool) -> Vec { let vm = self; - match varargs.len() { + match varargs.as_slice().len() { 0 => vec![], 1 => { let args0_repr = if str_single { @@ -369,6 +369,7 @@ impl VirtualMachine { vec![args0_repr] } _ => varargs + .as_slice() .iter() .map(|vararg| { vararg.repr(vm).unwrap_or_else(|_| { @@ -520,8 +521,8 @@ impl ExceptionCtor { (Self::Class(cls), _) => { let args = match_class!(match value { PyNone => vec![], - tup @ PyTuple => tup.to_vec(), - exc @ PyBaseException => exc.args().to_vec(), + tup @ PyTuple => tup.as_slice().to_vec(), + exc @ PyBaseException => exc.args().as_slice().to_vec(), obj => vec![obj], }); vm.invoke_exception(&cls, args) @@ -639,7 +640,7 @@ impl PyBaseException { } pub fn get_arg(&self, idx: usize) -> Option { - self.args.read().get(idx).cloned() + self.args.read().as_slice().get(idx).cloned() } } @@ -1279,6 +1280,7 @@ impl serde::Serialize for SerializeException<'_, '_> { fn serialize(&self, s: S) -> Result { s.collect_seq( self.1 + .as_slice() .iter() .map(|arg| crate::py_serde::PyObjectSerializer::new(self.0, arg)), ) @@ -2134,7 +2136,7 @@ pub(super) mod types { #[pymethod] fn __str__(zelf: &Py, vm: &VirtualMachine) -> PyStrRef { let args = zelf.args(); - if args.len() == 1 { + if args.as_slice().len() == 1 { vm.exception_args_as_string(&args, false) .into_iter() .exactly_one() @@ -2579,7 +2581,7 @@ pub(super) mod types { let obj = zelf.as_object().to_owned(); let mut result: Vec = vec![obj.class().to_owned().into()]; - if args.len() >= 2 && args.len() <= 5 { + if args.as_slice().len() >= 2 && args.as_slice().len() <= 5 { // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic let errno = zelf.get_arg(0).unwrap(); let msg = zelf.get_arg(1).unwrap(); @@ -2915,7 +2917,7 @@ pub(super) mod types { .clone() .downcast::() { - let location_tup_len = location_tuple.len(); + let location_tup_len = location_tuple.as_slice().len(); match location_tup_len { 4 | 6 | 7 => {} @@ -2948,7 +2950,7 @@ pub(super) mod types { .enumerate() { let value = if location_tup_len > i { - location_tuple[i].to_owned() + location_tuple.as_slice()[i].to_owned() } else { vm.ctx.none() }; @@ -3379,7 +3381,7 @@ fn check_except_star_type_valid(match_type: &PyObject, vm: &VirtualMachine) -> P // If it's a tuple, check each element if let Ok(tuple) = match_type.to_owned().downcast::() { - for item in tuple.iter() { + for item in tuple.as_slice() { check_one(item)?; } } else { @@ -3439,15 +3441,15 @@ pub fn exception_group_match( ))); } let pair_tuple: PyTupleRef = pair.try_into_value(vm)?; - if pair_tuple.len() < 2 { + if pair_tuple.as_slice().len() < 2 { return Err(vm.new_type_error(format!( "{}.split must return a 2-tuple, got tuple of size {}", exc_value.class().name(), - pair_tuple.len() + pair_tuple.as_slice().len() ))); } - let matched = pair_tuple[0].clone(); - let rest = pair_tuple[1].clone(); + let matched = pair_tuple.as_slice()[0].clone(); + let rest = pair_tuple.as_slice()[1].clone(); return Ok((rest, matched)); } @@ -3564,7 +3566,7 @@ fn collect_exception_group_leaf_ids( if let Ok(excs_attr) = exc.get_attr("exceptions", vm) && let Ok(tuple) = excs_attr.downcast::() { - for e in tuple.iter() { + for e in tuple.as_slice() { collect_exception_group_leaf_ids(e, leaf_ids, vm); } } @@ -3612,7 +3614,7 @@ fn split_by_leaf_ids(exc: &PyObject, leaf_ids: &HashSet, vm: &VirtualMach let tuple: PyTupleRef = excs_attr.try_into_value(vm)?; let mut matched = Vec::new(); - for e in tuple.iter() { + for e in tuple.as_slice() { let m = split_by_leaf_ids(e, leaf_ids, vm)?; if !vm.is_none(&m) { matched.push(m); diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 56690998621..c1c53cabbfe 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -4495,7 +4495,8 @@ impl ExecutingFrame<'_> { let fastlocals = self.localsplus.fastlocals_mut(); if let Some(closure) = closure { for i in 0..n { - fastlocals[freevar_start + i] = Some(closure[i].clone().into()); + fastlocals[freevar_start + i] = + Some(closure.as_slice()[i].clone().into()); } } } @@ -5235,12 +5236,16 @@ impl ExecutingFrame<'_> { }; // Check if we have enough match args - if match_args.len() < nargs_val { + if match_args.as_slice().len() < nargs_val { let type_name = type_name(); - let plural = if match_args.len() == 1 { "" } else { "s" }; + let plural = if match_args.as_slice().len() == 1 { + "" + } else { + "s" + }; return Err(vm.new_type_error(format!( "{type_name}() accepts {} positional sub-pattern{} ({} given)", - match_args.len(), + match_args.as_slice().len(), plural, nargs_val ))); @@ -5248,7 +5253,7 @@ impl ExecutingFrame<'_> { // Extract positional attributes for i in 0..nargs_val { - let attr_name = &match_args[i]; + let attr_name = &match_args.as_slice()[i]; let attr_name_str = match attr_name.downcast_ref::() { Some(s) => s, None => { @@ -7312,7 +7317,7 @@ impl ExecutingFrame<'_> { let kwarg_names_tuple = kwarg_names_obj .downcast_ref::() .expect("kwarg names should be tuple"); - let kw_count = kwarg_names_tuple.len(); + let kw_count = kwarg_names_tuple.as_slice().len(); let all_args: Vec = self.pop_multiple(nargs_usize).collect(); let self_or_null = self.pop_value_opt(); let callable = self.pop_value(); @@ -7370,7 +7375,7 @@ impl ExecutingFrame<'_> { let kwarg_names_tuple = kwarg_names_obj .downcast_ref::() .expect("kwarg names should be tuple"); - let kw_count = kwarg_names_tuple.len(); + let kw_count = kwarg_names_tuple.as_slice().len(); let all_args: Vec = self.pop_multiple(nargs_usize).collect(); self.pop_stackref_opt(); // null (self_or_null) self.pop_stackref(); // callable (bound method) @@ -7412,7 +7417,7 @@ impl ExecutingFrame<'_> { let kwarg_names_tuple = kwarg_names_obj .downcast_ref::() .expect("kwarg names should be tuple"); - let kw_count = kwarg_names_tuple.len(); + let kw_count = kwarg_names_tuple.as_slice().len(); let all_args: Vec = self.pop_multiple(nargs_usize).collect(); let self_or_null = self.pop_value_opt(); let callable = self.pop_value(); @@ -8931,7 +8936,7 @@ impl ExecutingFrame<'_> { let kwarg_names_tuple = kwarg_names_obj .downcast_ref::() .expect("kwarg names should be tuple"); - let kw_count = kwarg_names_tuple.len(); + let kw_count = kwarg_names_tuple.as_slice().len(); debug_assert!(kw_count <= nargs_usize, "CALL_KW kw_count exceeds nargs"); let stack_len = self.localsplus.stack_len(); @@ -11928,7 +11933,7 @@ impl ExecutingFrame<'_> { } let obj = self.top_value(); let new_op = if let Some(tuple) = obj.downcast_ref_if_exact::(vm) { - if tuple.len() != expected_count as usize { + if tuple.as_slice().len() != expected_count as usize { None } else if expected_count == 2 { Some(Instruction::UnpackSequenceTwoTuple) @@ -12292,10 +12297,10 @@ impl ExecutingFrame<'_> { .downcast() .map_err(|_| vm.new_type_error("TypeAlias expects a tuple argument"))?; - if tuple.len() != 3 { + if tuple.as_slice().len() != 3 { return Err(vm.new_type_error(format!( "TypeAlias expects exactly 3 arguments, got {}", - tuple.len() + tuple.as_slice().len() ))); } diff --git a/crates/vm/src/object/core.rs b/crates/vm/src/object/core.rs index 76d9fd102ff..1e5946cd0c9 100644 --- a/crates/vm/src/object/core.rs +++ b/crates/vm/src/object/core.rs @@ -461,7 +461,7 @@ pub struct Py { pub(super) typ: PyAtomicRef, // __class__ member - pub(super) payload: T, + pub(crate) payload: T, } pub const SIZEOF_PYOBJECT_HEAD: usize = core::mem::size_of::>(); @@ -3197,7 +3197,7 @@ mod tests { assert!(hierarchy.weakref_type.class().is(&hierarchy.type_type)); let object_bases = hierarchy.object_type.bases.read(); - assert!(object_bases.is_empty()); + assert!(object_bases.as_slice().is_empty()); assert!(object_bases.as_untyped().is(&hierarchy.empty_tuple)); assert!(object_bases.as_untyped().class().is(&hierarchy.tuple_type)); drop(object_bases); @@ -3208,8 +3208,8 @@ mod tests { &hierarchy.weakref_type, ] { let bases = typ.bases.read(); - assert_eq!(bases.len(), 1); - assert!(bases[0].is(&hierarchy.object_type)); + assert_eq!(bases.as_slice().len(), 1); + assert!(bases.as_slice()[0].is(&hierarchy.object_type)); assert!(bases.as_untyped().class().is(&hierarchy.tuple_type)); } } diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index e9abad45ff1..472f33c26f5 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -520,7 +520,7 @@ impl PyObject { return Ok(false); }; - let n = derived_bases.len(); + let n = derived_bases.as_slice().len(); match n { 0 => return Ok(false), 1 => { @@ -537,7 +537,7 @@ impl PyObject { } }; - let n = bases.len(); + let n = bases.as_slice().len(); // At this point we know n >= 2 debug_assert!(n >= 2); diff --git a/crates/vm/src/protocol/sequence.rs b/crates/vm/src/protocol/sequence.rs index 241f71398d6..32a1c47ed47 100644 --- a/crates/vm/src/protocol/sequence.rs +++ b/crates/vm/src/protocol/sequence.rs @@ -430,7 +430,7 @@ impl PySequence<'_> { F: FnMut(&PyObject) -> PyResult, { if let Some(tuple) = self.obj.downcast_ref_if_exact::(vm) { - tuple.iter().map(|x| f(x.as_ref())).collect() + tuple.as_slice().iter().map(|x| f(x.as_ref())).collect() } else if let Some(list) = self.obj.downcast_ref_if_exact::(vm) { list.borrow_vec().iter().map(|x| f(x.as_ref())).collect() } else { diff --git a/crates/vm/src/py_serde.rs b/crates/vm/src/py_serde.rs index d94330f3f7a..f6c91911cfb 100644 --- a/crates/vm/src/py_serde.rs +++ b/crates/vm/src/py_serde.rs @@ -86,7 +86,7 @@ impl serde::Serialize for PyObjectSerializer<'_> { } else if let Some(list) = self.pyobject.downcast_ref::() { serialize_seq_elements(serializer, &list.borrow_vec()) } else if let Some(tuple) = self.pyobject.downcast_ref::() { - serialize_seq_elements(serializer, tuple) + serialize_seq_elements(serializer, tuple.as_slice()) } else if self.pyobject.fast_isinstance(self.vm.ctx.types.dict_type) { let dict: PyDictRef = self.pyobject.to_owned().downcast().unwrap(); let pairs: Vec<_> = dict.into_iter().collect(); diff --git a/crates/vm/src/stdlib/_abc.rs b/crates/vm/src/stdlib/_abc.rs index b660e5e1440..99e1d1e655a 100644 --- a/crates/vm/src/stdlib/_abc.rs +++ b/crates/vm/src/stdlib/_abc.rs @@ -174,7 +174,7 @@ mod _abc { .downcast() .map_err(|_| vm.new_type_error("__bases__ is not a tuple"))?; - for base in bases.iter() { + for base in bases.as_slice() { if let Ok(base_abstracts) = base.get_attr("__abstractmethods__", vm) { let iter = base_abstracts.get_iter(vm)?; while let PyIterReturn::Return(key) = iter.next(vm)? { diff --git a/crates/vm/src/stdlib/_ast/constant.rs b/crates/vm/src/stdlib/_ast/constant.rs index a41aeadea08..4c46691961d 100644 --- a/crates/vm/src/stdlib/_ast/constant.rs +++ b/crates/vm/src/stdlib/_ast/constant.rs @@ -366,7 +366,7 @@ fn first_invalid_constant_type(vm: &VirtualMachine, value_object: &PyObject) -> obj.class().name() )) })?; - for item in tuple.iter() { + for item in tuple.as_slice() { if let Some(invalid_type) = first_invalid_constant_type_opt(vm, item)? { return Ok(invalid_type); } diff --git a/crates/vm/src/stdlib/_collections/ordered_dict.rs b/crates/vm/src/stdlib/_collections/ordered_dict.rs index b12e02630f8..7312784d9eb 100644 --- a/crates/vm/src/stdlib/_collections/ordered_dict.rs +++ b/crates/vm/src/stdlib/_collections/ordered_dict.rs @@ -1148,13 +1148,14 @@ pub(crate) mod ordered_dict { let Some(needle) = item.downcast_ref::() else { continue; }; - if needle.len() != 2 { + if needle.as_slice().len() != 2 { continue; } - let Some(found) = self.od.dict.inner_getitem_opt(&*needle[0], vm)? else { + let Some(found) = self.od.dict.inner_getitem_opt(&*needle.as_slice()[0], vm)? + else { continue; }; - if vm.identical_or_equal(&found, &needle[1])? { + if vm.identical_or_equal(&found, &needle.as_slice()[1])? { return Ok(false); } } @@ -1189,15 +1190,15 @@ pub(crate) mod ordered_dict { Some(needle) => needle, None => return Ok(false), }; - if needle.len() != 2 { + if needle.as_slice().len() != 2 { return Ok(false); } let zelf = PyOrderedDictItems::sequence_downcast(seq); - let key = &needle[0]; + let key = &needle.as_slice()[0]; let Some(found) = zelf.od.dict.inner_getitem_opt(&**key, vm)? else { return Ok(false); }; - vm.identical_or_equal(&found, &needle[1]) + vm.identical_or_equal(&found, &needle.as_slice()[1]) }), ..PySequenceMethods::NOT_IMPLEMENTED }; diff --git a/crates/vm/src/stdlib/_ctypes.rs b/crates/vm/src/stdlib/_ctypes.rs index 99c6b352e2e..54187d29213 100644 --- a/crates/vm/src/stdlib/_ctypes.rs +++ b/crates/vm/src/stdlib/_ctypes.rs @@ -50,7 +50,7 @@ impl Py { /// Mark all base classes as finalized fn mark_bases_final(&self) { - for base in self.bases.read().iter() { + for base in self.bases.read().as_slice() { if let Some(mut stg) = base.get_type_data_mut::() { stg.flags |= StgInfoFlags::DICTFLAG_FINAL; } else { @@ -713,7 +713,7 @@ pub(crate) mod _ctypes { let mut max_align = 1usize; for field in &fields { if let Some(tuple) = field.downcast_ref::() - && let Some(field_type) = tuple.get(1) + && let Some(field_type) = tuple.as_slice().get(1) { let align = if let Ok(ft) = field_type.clone().downcast::() { @@ -932,9 +932,9 @@ pub(crate) mod _ctypes { return Err(vm.new_value_error("NULL function pointer")); } - let mut call_args = Vec::with_capacity(args.len()); + let mut call_args = Vec::with_capacity(args.as_slice().len()); - for arg in args.iter() { + for arg in args.as_slice() { if vm.is_none(arg) { call_args.push(rustpython_host_env::ctypes::CdeclArgValue::Pointer(0)); } else if let Ok(int_val) = arg.try_int(vm) { diff --git a/crates/vm/src/stdlib/_ctypes/base.rs b/crates/vm/src/stdlib/_ctypes/base.rs index 2f4a374f5be..715967cb349 100644 --- a/crates/vm/src/stdlib/_ctypes/base.rs +++ b/crates/vm/src/stdlib/_ctypes/base.rs @@ -940,7 +940,7 @@ impl PyCData { { let items: Option> = if let Some(tuple) = value.downcast_ref::() { - Some(tuple.to_vec()) + Some(tuple.as_slice().to_vec()) } else { value .downcast_ref::() @@ -953,6 +953,7 @@ impl PyCData { let exc_name = e.class().name().to_string(); let exc_args = e.args(); let exc_msg = exc_args + .as_slice() .first() .and_then(|a| a.downcast_ref::().map(|s| s.to_string())) .unwrap_or_default(); @@ -2196,7 +2197,7 @@ fn make_fields( let fieldlist: Vec = if let Some(list) = fields.downcast_ref::() { list.borrow_vec().to_vec() } else if let Some(tuple) = fields.downcast_ref::() { - tuple.to_vec() + tuple.as_slice().to_vec() } else { return Err(vm.new_type_error("_fields_ must be a sequence")); }; @@ -2206,11 +2207,12 @@ fn make_fields( .downcast_ref::() .ok_or_else(|| vm.new_type_error("_fields_ must contain tuples"))?; - if field_tuple.len() < 2 { + if field_tuple.as_slice().len() < 2 { continue; } let fname = field_tuple + .as_slice() .first() .expect("len checked") .downcast_ref::() @@ -2255,7 +2257,7 @@ pub(super) fn make_anon_fields(cls: &Py, vm: &VirtualMachine) -> PyResul let anon_names: Vec = if let Some(list) = anon.downcast_ref::() { list.borrow_vec().to_vec() } else if let Some(tuple) = anon.downcast_ref::() { - tuple.to_vec() + tuple.as_slice().to_vec() } else { return Err(vm.new_type_error("_anonymous_ must be a sequence")); }; diff --git a/crates/vm/src/stdlib/_ctypes/function.rs b/crates/vm/src/stdlib/_ctypes/function.rs index 0770880161d..77a66aca7c9 100644 --- a/crates/vm/src/stdlib/_ctypes/function.rs +++ b/crates/vm/src/stdlib/_ctypes/function.rs @@ -745,6 +745,7 @@ impl Constructor for PyCFuncPtr { // Check if first argument is a tuple (name, dll) form if let Some(tuple) = first_arg.downcast_ref::() { let name = tuple + .as_slice() .first() .ok_or_else(|| vm.new_type_error("Expected a tuple with at least 2 elements"))? .downcast_ref::() @@ -1080,14 +1081,16 @@ fn parse_paramflags( return (direction, None, None); }; let direction = tuple + .as_slice() .first() .and_then(|d| d.try_int(vm).ok()) .and_then(|i| i.as_bigint().to_u32()) .unwrap_or(1); let name = tuple + .as_slice() .get(1) .and_then(|n| n.downcast_ref::().map(|s| s.to_string())); - let default = tuple.get(2).cloned(); + let default = tuple.as_slice().get(2).cloned(); (direction, name, default) }) .collect(); diff --git a/crates/vm/src/stdlib/_ctypes/simple.rs b/crates/vm/src/stdlib/_ctypes/simple.rs index 5b8dfb94244..e6e54e4f66f 100644 --- a/crates/vm/src/stdlib/_ctypes/simple.rs +++ b/crates/vm/src/stdlib/_ctypes/simple.rs @@ -1071,6 +1071,7 @@ impl Representable for PyCSimple { // vs subclass of simple type (like class X(c_int): pass) let bases = cls.bases.read(); let is_direct_simple = bases + .as_slice() .iter() .any(|base| base.name().to_string() == "_SimpleCData"); @@ -1249,6 +1250,7 @@ impl PyCSimple { let bases = cls.bases.read(); // If base is NOT _SimpleCData, then it's a subclass of a subclass !bases + .as_slice() .iter() .any(|base| base.name().to_string() == "_SimpleCData") }; diff --git a/crates/vm/src/stdlib/_ctypes/structure.rs b/crates/vm/src/stdlib/_ctypes/structure.rs index ad3f8be20fb..6486e1dd788 100644 --- a/crates/vm/src/stdlib/_ctypes/structure.rs +++ b/crates/vm/src/stdlib/_ctypes/structure.rs @@ -18,7 +18,7 @@ pub(super) fn calculate_struct_size(cls: &Py, vm: &VirtualMachine) -> Py for field in &fields { if let Some(tuple) = field.downcast_ref::() - && let Some(field_type) = tuple.get(1) + && let Some(field_type) = tuple.as_slice().get(1) { total_size += super::_ctypes::sizeof(field_type.clone(), vm)?; } @@ -90,7 +90,7 @@ impl Initializer for PyCStructType { // No _fields_ defined - try to copy from base class (PyCStgInfo_clone) let (has_base_info, base_clone) = { let bases = new_type.bases.read(); - if let Some(base) = bases.first() { + if let Some(base) = bases.as_slice().first() { (base.stg_info_opt().is_some(), Some(base.clone())) } else { (false, None) @@ -243,7 +243,7 @@ impl PyCStructType { let fields: Vec = if let Some(list) = fields_attr.downcast_ref::() { list.borrow_vec().to_vec() } else if let Some(tuple) = fields_attr.downcast_ref::() { - tuple.to_vec() + tuple.as_slice().to_vec() } else { return Err(vm.new_type_error("_fields_ must be a list or tuple")); }; @@ -283,7 +283,7 @@ impl PyCStructType { mut field_layouts, ) = { let bases = cls.bases.read(); - if let Some(base) = bases.first() + if let Some(base) = bases.as_slice().first() && let Some(baseinfo) = base.stg_info_opt() { ( @@ -313,13 +313,14 @@ impl PyCStructType { .downcast_ref::() .ok_or_else(|| vm.new_type_error("_fields_ must contain tuples"))?; - if field_tuple.len() < 2 { + if field_tuple.as_slice().len() < 2 { return Err( vm.new_type_error("_fields_ tuple must have at least 2 elements (name, type)") ); } let name = field_tuple + .as_slice() .first() .expect("len checked") .downcast_ref::() @@ -327,7 +328,7 @@ impl PyCStructType { .as_str() .to_owned(); - let field_type = field_tuple.get(1).expect("len checked").clone(); + let field_type = field_tuple.as_slice().get(1).expect("len checked").clone(); // For swapped byte order structures, validate field type supports byte swapping if is_swapped { @@ -425,8 +426,8 @@ impl PyCStructType { .map_err(|_| vm.new_type_error("_fields_ type must be a ctypes type"))?; // Check for bitfield size (optional 3rd element in tuple) - let (c_field, field_advances_offset) = if field_tuple.len() > 2 { - let bit_size_obj = field_tuple.get(2).expect("len checked"); + let (c_field, field_advances_offset) = if field_tuple.as_slice().len() > 2 { + let bit_size_obj = field_tuple.as_slice().get(2).expect("len checked"); let bit_size = bit_size_obj .try_int(vm)? .as_bigint() @@ -723,7 +724,7 @@ impl PyCStructure { // 1. First process base class fields recursively let base_clone = { let bases = type_obj.bases.read(); - if let Some(base) = bases.first() + if let Some(base) = bases.as_slice().first() && base.stg_info_opt().is_some() { Some(base.clone()) @@ -745,7 +746,7 @@ impl PyCStructure { break; } if let Some(tuple) = field.downcast_ref::() - && let Some(name) = tuple.first() + && let Some(name) = tuple.as_slice().first() && let Some(name_str) = name.downcast_ref::() { let field_name = name_str.as_wtf8().to_owned(); diff --git a/crates/vm/src/stdlib/_ctypes/union.rs b/crates/vm/src/stdlib/_ctypes/union.rs index 141cf853398..7e424294f0d 100644 --- a/crates/vm/src/stdlib/_ctypes/union.rs +++ b/crates/vm/src/stdlib/_ctypes/union.rs @@ -18,7 +18,7 @@ pub(super) fn calculate_union_size(cls: &Py, vm: &VirtualMachine) -> PyR for field in &fields { if let Some(tuple) = field.downcast_ref::() - && let Some(field_type) = tuple.get(1) + && let Some(field_type) = tuple.as_slice().get(1) { let field_size = super::_ctypes::sizeof(field_type.clone(), vm)?; max_size = max_size.max(field_size); @@ -94,7 +94,7 @@ impl Initializer for PyCUnionType { // No _fields_ defined - try to copy from base class let (has_base_info, base_clone) = { let bases = new_type.bases.read(); - if let Some(base) = bases.first() { + if let Some(base) = bases.as_slice().first() { (base.stg_info_opt().is_some(), Some(base.clone())) } else { (false, None) @@ -159,7 +159,7 @@ impl PyCUnionType { let fields: Vec = if let Some(list) = fields_attr.downcast_ref::() { list.borrow_vec().to_vec() } else if let Some(tuple) = fields_attr.downcast_ref::() { - tuple.to_vec() + tuple.as_slice().to_vec() } else { return Err(vm.new_type_error("_fields_ must be a list or tuple")); }; @@ -188,7 +188,7 @@ impl PyCUnionType { // Note: Union fields always start at offset 0, but we inherit base size/align let (mut max_size, mut max_align, mut has_pointer, mut has_bitfield, mut field_layouts) = { let bases = cls.bases.read(); - if let Some(base) = bases.first() + if let Some(base) = bases.as_slice().first() && let Some(baseinfo) = base.stg_info_opt() { ( @@ -208,13 +208,14 @@ impl PyCUnionType { .downcast_ref::() .ok_or_else(|| vm.new_type_error("_fields_ must contain tuples"))?; - if field_tuple.len() < 2 { + if field_tuple.as_slice().len() < 2 { return Err( vm.new_type_error("_fields_ tuple must have at least 2 elements (name, type)") ); } let name = field_tuple + .as_slice() .first() .expect("len checked") .downcast_ref::() @@ -222,7 +223,7 @@ impl PyCUnionType { .as_str() .to_owned(); - let field_type = field_tuple.get(1).expect("len checked").clone(); + let field_type = field_tuple.as_slice().get(1).expect("len checked").clone(); // For swapped byte order unions, validate field type supports byte swapping if is_swapped { @@ -279,8 +280,8 @@ impl PyCUnionType { // Check for bitfield size (optional 3rd element in tuple) // For unions, each field starts fresh (CPython: _layout.py) - let c_field = if field_tuple.len() > 2 { - let bit_size_obj = field_tuple.get(2).expect("len checked"); + let c_field = if field_tuple.as_slice().len() > 2 { + let bit_size_obj = field_tuple.as_slice().get(2).expect("len checked"); let bit_size = bit_size_obj .try_int(vm)? .as_bigint() @@ -591,7 +592,7 @@ impl PyCUnion { // Recurse if base has StgInfo let base_clone = { let bases = type_obj.bases.read(); - if let Some(base) = bases.first() && + if let Some(base) = bases.as_slice().first() && // Check if base has StgInfo base.stg_info_opt().is_some() { @@ -614,7 +615,7 @@ impl PyCUnion { break; } if let Some(tuple) = field.downcast_ref::() - && let Some(name) = tuple.first() + && let Some(name) = tuple.as_slice().first() && let Some(name_str) = name.downcast_ref::() { let field_name = name_str.as_wtf8().to_owned(); diff --git a/crates/vm/src/stdlib/_functools.rs b/crates/vm/src/stdlib/_functools.rs index 56ef497bc67..3d2ebf3f611 100644 --- a/crates/vm/src/stdlib/_functools.rs +++ b/crates/vm/src/stdlib/_functools.rs @@ -305,17 +305,17 @@ mod _functools { .downcast::() .map_err(|_| vm.new_type_error("argument to __setstate__ must be a tuple"))?; - if state_tuple.len() != 4 { + if state_tuple.as_slice().len() != 4 { return Err(vm.new_type_error(format!( "expected 4 items in state, got {}", - state_tuple.len() + state_tuple.as_slice().len() ))); } - let func = &state_tuple[0]; - let args = &state_tuple[1]; - let kwds = &state_tuple[2]; - let dict = &state_tuple[3]; + let func = &state_tuple.as_slice()[0]; + let args = &state_tuple.as_slice()[1]; + let kwds = &state_tuple.as_slice()[2]; + let dict = &state_tuple.as_slice()[3]; if !func.is_callable() { return Err(vm.new_type_error("invalid partial state")); @@ -540,7 +540,8 @@ mod _functools { } // Build combined args, replacing placeholders - let mut combined_args = Vec::with_capacity(stored_args.len() + args.args.len()); + let mut combined_args = + Vec::with_capacity(stored_args.as_slice().len() + args.args.len()); let mut new_args_iter = args.args.iter(); for stored_arg in stored_args.as_slice() { diff --git a/crates/vm/src/stdlib/_interpreters.rs b/crates/vm/src/stdlib/_interpreters.rs index 4b7c3700e15..903a14f6acc 100644 --- a/crates/vm/src/stdlib/_interpreters.rs +++ b/crates/vm/src/stdlib/_interpreters.rs @@ -784,6 +784,7 @@ pub(crate) mod _interpreters { if !o .downcast_ref::() .unwrap() + .as_slice() .is_empty() => { Some(SharedValue::from_object(o, Fallback::Full, vm)?) diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index 8daa28423f2..42c6654dd76 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -2890,10 +2890,10 @@ mod _io { } let res = self.encode.call(args, vm)?; let tuple: PyTupleRef = res.try_into_value(vm)?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_type_error("encoder must return a tuple (object, integer)")); } - Ok(tuple[0].clone()) + Ok(tuple.as_slice()[0].clone()) } #[pymethod] @@ -2935,10 +2935,10 @@ mod _io { } let res = self.decode.call(args, vm)?; let tuple: PyTupleRef = res.try_into_value(vm)?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_type_error("decoder must return a tuple (object, integer)")); } - Ok(tuple[0].clone()) + Ok(tuple.as_slice()[0].clone()) } #[pymethod] @@ -4889,20 +4889,20 @@ mod _io { if zelf.closed.load() { return Err(vm.new_value_error("__setstate__ on closed file")); } - if state.len() != 4 { + if state.as_slice().len() != 4 { return Err(vm.new_type_error(format!( "__setstate__ argument should be 4-tuple, got {}", - state.len() + state.as_slice().len() ))); } - let content: PyStrRef = state[0].clone().try_into_value(vm)?; - let newline = Newlines::try_from_object(vm, state[1].clone())?; - let pos: isize = isize::try_from_object(vm, state[2].clone())?; + let content: PyStrRef = state.as_slice()[0].clone().try_into_value(vm)?; + let newline = Newlines::try_from_object(vm, state.as_slice()[1].clone())?; + let pos: isize = isize::try_from_object(vm, state.as_slice()[2].clone())?; if pos < 0 { return Err(vm.new_value_error("negative seek position")); } - let dict = &state[3]; + let dict = &state.as_slice()[3]; // Set content and position let raw_bytes = content.as_bytes().to_vec(); @@ -5162,16 +5162,16 @@ mod _io { if zelf.closed.load() { return Err(vm.new_value_error("__setstate__ on closed file")); } - if object.len() != 3 { + if object.as_slice().len() != 3 { return Err(vm.new_type_error(format!( "__setstate__ argument should be 3-tuple, got {}", - object.len() + object.as_slice().len() ))); } - let content: PyBytesRef = object[0].clone().try_into_value(vm)?; - let pos: u64 = object[1].clone().try_into_value(vm)?; - let dict = &object[2]; + let content: PyBytesRef = object.as_slice()[0].clone().try_into_value(vm)?; + let pos: u64 = object.as_slice()[1].clone().try_into_value(vm)?; + let dict = &object.as_slice()[2]; // Check exports and set content (like CHECK_EXPORTS) let mut buffer = zelf.try_resizable(vm)?; diff --git a/crates/vm/src/stdlib/_testinternalcapi.rs b/crates/vm/src/stdlib/_testinternalcapi.rs index e5412dda7f8..25adf924c22 100644 --- a/crates/vm/src/stdlib/_testinternalcapi.rs +++ b/crates/vm/src/stdlib/_testinternalcapi.rs @@ -1774,8 +1774,8 @@ fn py_to_constant_data( } if obj.class().is(vm.ctx.types.tuple_type) { let tuple = obj.downcast::().unwrap(); - let mut elements = Vec::with_capacity(tuple.len()); - for item in tuple.iter() { + let mut elements = Vec::with_capacity(tuple.as_slice().len()); + for item in tuple.as_slice() { elements.push(py_to_constant_data(item.clone(), vm)?); } return Ok(ConstantData::Tuple { elements }); diff --git a/crates/vm/src/stdlib/_thread.rs b/crates/vm/src/stdlib/_thread.rs index 8ad2885ce6c..ce7983ea17b 100644 --- a/crates/vm/src/stdlib/_thread.rs +++ b/crates/vm/src/stdlib/_thread.rs @@ -701,7 +701,7 @@ pub(crate) mod _thread { } let args = FuncArgs::new( - args.to_vec(), + args.as_slice().to_vec(), kwargs .map_or_else( || Ok(Default::default()), diff --git a/crates/vm/src/stdlib/_typing.rs b/crates/vm/src/stdlib/_typing.rs index f1a3a8a7365..377e793463a 100644 --- a/crates/vm/src/stdlib/_typing.rs +++ b/crates/vm/src/stdlib/_typing.rs @@ -165,7 +165,7 @@ pub(crate) mod decl { /// Handles tuples specially by wrapping in parentheses. fn typing_type_repr_value(value: &PyObject, vm: &VirtualMachine) -> PyResult { if let Ok(tuple) = value.try_to_ref::(vm) { - let mut parts = Vec::with_capacity(tuple.len()); + let mut parts = Vec::with_capacity(tuple.as_slice().len()); for item in tuple { parts.push(typing_type_repr(item, vm)?); } @@ -281,7 +281,7 @@ pub(crate) mod decl { } fn __getitem__(zelf: &Py, args: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if zelf.type_params.is_empty() { + if zelf.type_params.as_slice().is_empty() { return Err(vm.new_type_error("Only generic type aliases are subscriptable")); } let args_tuple = if let Ok(tuple) = args.try_to_ref::(vm) { @@ -318,7 +318,7 @@ pub(crate) mod decl { type_params: &Py, vm: &VirtualMachine, ) -> PyResult> { - if type_params.is_empty() { + if type_params.as_slice().is_empty() { return Ok(None); } let no_default = &vm.ctx.typing_no_default; @@ -473,6 +473,7 @@ pub(crate) mod decl { vm: &VirtualMachine, ) -> PyResult { let has_tvt = type_params + .as_slice() .iter() .any(|p| p.downcastable::()); if !has_tvt { @@ -481,6 +482,7 @@ pub(crate) mod decl { let typing = vm.import("typing", 0)?; let unpack_cls = typing.get_attr("Unpack", vm)?; let new_params: Vec = type_params + .as_slice() .iter() .map(|p| { if p.downcastable::() { diff --git a/crates/vm/src/stdlib/_warnings.rs b/crates/vm/src/stdlib/_warnings.rs index 4349add70d0..8a8f92b77da 100644 --- a/crates/vm/src/stdlib/_warnings.rs +++ b/crates/vm/src/stdlib/_warnings.rs @@ -137,7 +137,7 @@ mod _warnings { // Validate skip_file_prefixes: each element must be a str let skip_prefixes = args.skip_file_prefixes.into_option(); if let Some(ref prefixes) = skip_prefixes { - for item in prefixes.iter() { + for item in prefixes.as_slice() { if !item.class().is(vm.ctx.types.str_type) { return Err(vm.new_type_error("skip_file_prefixes must be a tuple of strs")); } diff --git a/crates/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs index e605fc3d9ad..ba7170fc9d2 100644 --- a/crates/vm/src/stdlib/builtins.rs +++ b/crates/vm/src/stdlib/builtins.rs @@ -504,7 +504,7 @@ mod builtins { )) })? .into_pyref(); - if closure_tuple.len() != num_free { + if closure_tuple.as_slice().len() != num_free { return Err(vm.new_type_error(format!( "code object requires a closure of exactly length {num_free}" ))); @@ -1294,7 +1294,7 @@ mod builtins { // Update bases. let mut new_bases: Option> = None; let bases = PyTuple::new_ref(bases.into_vec(), &vm.ctx); - for (i, base) in bases.iter().enumerate() { + for (i, base) in bases.as_slice().iter().enumerate() { if base.fast_isinstance(vm.ctx.types.type_type) { if let Some(bases) = &mut new_bases { bases.push(base.clone()); @@ -1314,8 +1314,8 @@ mod builtins { let entries: PyTupleRef = entries .downcast() .map_err(|_| vm.new_type_error("__mro_entries__ must return a tuple"))?; - let new_bases = new_bases.get_or_insert_with(|| bases[..i].to_vec()); - new_bases.extend_from_slice(&entries); + let new_bases = new_bases.get_or_insert_with(|| bases.as_slice()[..i].to_vec()); + new_bases.extend_from_slice(entries.as_slice()); } let new_bases = new_bases.map(|v| PyTuple::new_ref(v, &vm.ctx)); @@ -1328,10 +1328,10 @@ mod builtins { let metaclass = kwargs.pop_kwarg("metaclass").map_or_else( || { // if there are no bases, use type; else get the type of the first base - Ok(if bases.is_empty() { + Ok(if bases.as_slice().is_empty() { vm.ctx.types.type_type.to_owned() } else { - bases.first().unwrap().class().to_owned() + bases.as_slice().first().unwrap().class().to_owned() }) }, |metaclass| { @@ -1343,7 +1343,7 @@ mod builtins { let (metaclass, meta_name) = match metaclass { Ok(mut metaclass) => { - for base in bases.iter() { + for base in bases.as_slice() { let base_class = base.class(); // if winner is subtype of tmptype, continue (winner is more derived) if metaclass.fast_issubclass(base_class) { @@ -1390,7 +1390,7 @@ mod builtins { .as_object() .get_attr(identifier!(vm, __type_params__), vm) && let Some(type_params_tuple) = type_params.downcast_ref::() - && !type_params_tuple.is_empty() + && !type_params_tuple.as_slice().is_empty() { // Set .type_params in namespace so the compiler-generated code can use it namespace @@ -1423,7 +1423,7 @@ mod builtins { .as_object() .get_attr(identifier!(vm, __type_params__), vm) && let Some(type_params_tuple) = type_params.downcast_ref::() - && !type_params_tuple.is_empty() + && !type_params_tuple.as_slice().is_empty() { class.set_attr(identifier!(vm, __type_params__), type_params.clone(), vm)?; // Also set __parameters__ for compatibility with typing module diff --git a/crates/vm/src/stdlib/marshal.rs b/crates/vm/src/stdlib/marshal.rs index e87564e659e..2980337c26b 100644 --- a/crates/vm/src/stdlib/marshal.rs +++ b/crates/vm/src/stdlib/marshal.rs @@ -355,12 +355,12 @@ mod decl { buf.write_slice(&data); } else if let Some(t) = obj.downcast_ref::() { // From 4 on a short tuple carries its length in a single byte. - if version >= 4 && t.len() < 256 { + if version >= 4 && t.as_slice().len() < 256 { buf.write_u8(b')'); - buf.write_u8(t.len() as u8); + buf.write_u8(t.as_slice().len() as u8); } else { buf.write_u8(b'('); - buf.write_u32(t.len() as u32); + buf.write_u32(t.as_slice().len() as u32); } for elem in t.as_slice() { write_object_depth(buf, elem, refs, version, allow_code, vm, depth - 1)?; @@ -590,7 +590,7 @@ mod decl { .ok_or(marshal::MarshalError::BadType)?; // SAFETY: compiler-core calls this only on a fresh placeholder, // once per index, before returning it to Python code. - unsafe { tuple.set_marshal_item(index, value) }; + unsafe { tuple.payload.set_marshal_item(index, value) }; Ok(()) } fn make_code(&self, code: CodeObject) -> Result { diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 5760dc932da..85ea738f636 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -1432,7 +1432,7 @@ pub(super) mod _os { vm, )?; let tuple = result.downcast_ref::().unwrap(); - let mut items: Vec = tuple.to_vec(); + let mut items: Vec = tuple.as_slice().to_vec(); // Copy integer time fields to hidden float timestamp slots when not provided. // indices 7-9: st_atime_int, st_mtime_int, st_ctime_int @@ -1835,10 +1835,10 @@ pub(super) mod _os { #[pyfunction] fn utime(args: UtimeArgs<'_>, vm: &VirtualMachine) -> PyResult<()> { let parse_tup = |tup: &Py| -> Option<(PyObjectRef, PyObjectRef)> { - if tup.len() != 2 { + if tup.as_slice().len() != 2 { None } else { - Some((tup[0].clone(), tup[1].clone())) + Some((tup.as_slice()[0].clone(), tup.as_slice()[1].clone())) } }; let (acc, modif) = match (args.times, args.ns) { diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 47e21a41a5c..276f0c4fc29 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -1797,7 +1797,7 @@ pub mod module { let Some(tuple) = obj.downcast_ref::() else { return Err(vm.new_type_error("scheduler must be a tuple or None")); }; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_type_error("A scheduler tuple must have two elements")); } Err(vm.new_not_implemented_error("The scheduler option is not supported in this system.")) @@ -1831,7 +1831,7 @@ pub mod module { if let Some(it) = self.file_actions { for action in it.iter(vm)? { let action = action?; - let (id, args) = action.split_first().ok_or_else(|| { + let (id, args) = action.as_slice().split_first().ok_or_else(|| { vm.new_type_error("Each file_actions element must be a non-empty tuple") })?; let id = i32::try_from_borrowed_object(vm, id)?; diff --git a/crates/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs index b68cb1b8853..482114f36f0 100644 --- a/crates/vm/src/stdlib/time.rs +++ b/crates/vm/src/stdlib/time.rs @@ -131,7 +131,7 @@ mod decl { let seconds_type_name = object.class().name().to_owned(); let dur = object.try_into_value::(vm).map_err(|e| { if e.class().is(vm.ctx.exceptions.value_error) - && let Some(s) = e.args().first().and_then(|arg| arg.str(vm).ok()) + && let Some(s) = e.args().as_slice().first().and_then(|arg| arg.str(vm).ok()) && s.as_bytes() == b"negative duration" { return vm.new_value_error("sleep length must be non-negative"); diff --git a/crates/vm/src/stdlib/typevar.rs b/crates/vm/src/stdlib/typevar.rs index e46268893b7..ef62a689d9e 100644 --- a/crates/vm/src/stdlib/typevar.rs +++ b/crates/vm/src/stdlib/typevar.rs @@ -258,13 +258,13 @@ pub(crate) mod typevar { // Find our index in parameters let self_obj: PyObjectRef = zelf.to_owned().into(); - let param_index = params_tuple.iter().position(|p| p.is(&self_obj)); + let param_index = params_tuple.as_slice().iter().position(|p| p.is(&self_obj)); if let Some(index) = param_index { // Check if we have enough arguments - if args_tuple.len() <= index && zelf.has_default(vm) { + if args_tuple.as_slice().len() <= index && zelf.has_default(vm) { // Need to add default value - let mut new_args = args_tuple.iter().cloned().collect::>(); + let mut new_args = args_tuple.as_slice().to_vec(); // Add default value at the correct position while new_args.len() <= index { diff --git a/crates/vm/src/suggestion.rs b/crates/vm/src/suggestion.rs index 7f3077b9e7a..c21b295358e 100644 --- a/crates/vm/src/suggestion.rs +++ b/crates/vm/src/suggestion.rs @@ -75,7 +75,9 @@ pub fn offer_suggestions(exc: &Py, vm: &VirtualMachine) -> Opti let tb = tb.iter().last().unwrap_or(tb); let varnames = tb.frame.iframe().code().to_owned().co_varnames(vm); - if let Some(suggestions) = calculate_suggestions(varnames.iter().map(|o| &**o), &name) { + if let Some(suggestions) = + calculate_suggestions(varnames.as_slice().iter().map(|o| &**o), &name) + { return Some(suggestions); }; diff --git a/crates/vm/src/types/structseq.rs b/crates/vm/src/types/structseq.rs index ae7cdcd0504..ed5edfa5d80 100644 --- a/crates/vm/src/types/structseq.rs +++ b/crates/vm/src/types/structseq.rs @@ -19,7 +19,10 @@ use crate::{ const DEFAULT_STRUCTSEQ_REDUCE: PyMethodDef = PyMethodDef::new_const( "__reduce__", |zelf: PyRef, vm: &VirtualMachine| -> PyTupleRef { - vm.new_tuple((zelf.class().to_owned(), (vm.ctx.new_tuple(zelf.to_vec()),))) + vm.new_tuple(( + zelf.class().to_owned(), + (vm.ctx.new_tuple(zelf.as_slice().to_vec()),), + )) }, PyMethodFlags::METHOD, Some("__reduce__($self, /)\n--\n\n"), @@ -157,7 +160,7 @@ static STRUCT_SEQUENCE_AS_SEQUENCE: LazyLock = // Convert to visible-only tuple, then use regular tuple concat let n_seq = get_visible_len(seq.obj, vm)?; let tuple = seq.obj.downcast_ref::().unwrap(); - let visible: Vec<_> = tuple.iter().take(n_seq).cloned().collect(); + let visible: Vec<_> = tuple.as_slice().iter().take(n_seq).cloned().collect(); let visible_tuple = PyTuple::new_ref(visible, &vm.ctx); // Use tuple's concat implementation visible_tuple @@ -169,7 +172,7 @@ static STRUCT_SEQUENCE_AS_SEQUENCE: LazyLock = // Convert to visible-only tuple, then use regular tuple repeat let n_seq = get_visible_len(seq.obj, vm)?; let tuple = seq.obj.downcast_ref::().unwrap(); - let visible: Vec<_> = tuple.iter().take(n_seq).cloned().collect(); + let visible: Vec<_> = tuple.as_slice().iter().take(n_seq).cloned().collect(); let visible_tuple = PyTuple::new_ref(visible, &vm.ctx); // Use tuple's repeat implementation visible_tuple.as_object().sequence_unchecked().repeat(n, vm) @@ -189,12 +192,12 @@ static STRUCT_SEQUENCE_AS_SEQUENCE: LazyLock = if idx >= n_seq { return Err(vm.new_index_error("tuple index out of range")); } - Ok(tuple[idx].clone()) + Ok(tuple.as_slice()[idx].clone()) }), contains: atomic_func!(|seq, needle, vm| { let n_seq = get_visible_len(seq.obj, vm)?; let tuple = seq.obj.downcast_ref::().unwrap(); - for item in tuple.iter().take(n_seq) { + for item in tuple.as_slice().iter().take(n_seq) { if item.rich_compare_bool(needle, PyComparisonOp::Eq, vm)? { return Ok(true); } @@ -292,6 +295,7 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static { let (body, suffix) = if let Some(_guard) = rustpython_vm::recursion::ReprGuard::enter(vm, zelf.as_ref()) { let fields: PyResult> = zelf + .as_slice() .iter() .map(|value| value.as_ref()) .zip(field_names.iter().copied()) @@ -468,7 +472,7 @@ fn struct_sequence_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { .downcast_ref::() .ok_or_else(|| vm.new_type_error("expected tuple"))?; let n_seq = get_visible_len(&zelf, vm)?; - let visible: Vec<_> = tuple.iter().take(n_seq).cloned().collect(); + let visible: Vec<_> = tuple.as_slice().iter().take(n_seq).cloned().collect(); let visible_tuple = PyTuple::new_ref(visible, &vm.ctx); visible_tuple .as_object() @@ -487,7 +491,7 @@ fn struct_sequence_hash( .ok_or_else(|| vm.new_type_error("expected tuple"))?; let n_seq = get_visible_len(zelf, vm)?; // Create a visible-only tuple and hash it - let visible: Vec<_> = tuple.iter().take(n_seq).cloned().collect(); + let visible: Vec<_> = tuple.as_slice().iter().take(n_seq).cloned().collect(); let visible_tuple = PyTuple::new_ref(visible, &vm.ctx); visible_tuple.as_object().hash(vm) } @@ -510,7 +514,7 @@ fn struct_sequence_richcompare( let zelf_len = get_visible_len(zelf, vm)?; // For other, try to get visible len; if it fails (not a struct sequence), use full length - let other_len = get_visible_len(other, vm).unwrap_or(other_tuple.len()); + let other_len = get_visible_len(other, vm).unwrap_or(other_tuple.as_slice().len()); let zelf_visible = &zelf_tuple.as_slice()[..zelf_len]; let other_visible = &other_tuple.as_slice()[..other_len]; diff --git a/crates/vm/src/vm/crossinterp.rs b/crates/vm/src/vm/crossinterp.rs index 56bc7696da5..bce4264493f 100644 --- a/crates/vm/src/vm/crossinterp.rs +++ b/crates/vm/src/vm/crossinterp.rs @@ -214,7 +214,8 @@ impl SharedValue { if cls.is(vm.ctx.types.tuple_type) { let t = obj.downcast_ref::().unwrap(); return Some( - t.iter() + t.as_slice() + .iter() .map(|item| { vm.with_recursion("while sharing a tuple", || { Self::from_object(item, fallback, vm) @@ -626,13 +627,20 @@ fn verify_stateless_function(func: &Py, vm: &VirtualMachine) -> PyRe render_repr(&func.builtins, vm) )) })?; - if func.__defaults__().is_some_and(|d| !d.is_empty()) { + if func + .__defaults__() + .is_some_and(|d| !d.as_slice().is_empty()) + { return Err(vm.new_value_error("defaults not supported")); } if func.__kwdefaults__().is_some_and(|d| !d.is_empty()) { return Err(vm.new_value_error("keyword defaults not supported")); } - if func.closure.as_ref().is_some_and(|c| !c.is_empty()) { + if func + .closure + .as_ref() + .is_some_and(|c| !c.as_slice().is_empty()) + { return Err(vm.new_value_error("closures not supported")); } verify_stateless(&func.code, Some((&func.globals, builtins)), vm) diff --git a/crates/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs index 02916c16135..ae986145c7c 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -3142,7 +3142,7 @@ impl VirtualMachine { let fromlist_empty = self.is_none(&from_list) || from_list .downcast_ref::() - .is_some_and(|tuple| tuple.is_empty()); + .is_some_and(|tuple| tuple.as_slice().is_empty()); if level == 0 && fromlist_empty && builtins.is(self.builtins.dict().as_object()) @@ -3392,7 +3392,7 @@ impl VirtualMachine { i += 1; } } - ref t @ PyTuple => Ok(t.iter().cloned().map(f).collect()), + ref t @ PyTuple => Ok(t.as_slice().iter().cloned().map(f).collect()), // TODO: put internal iterable type obj => { Ok(self.map_py_iter(obj, hint, f)) diff --git a/crates/vm/src/warn.rs b/crates/vm/src/warn.rs index 1e9888ce7a4..3d8e7cb6fb1 100644 --- a/crates/vm/src/warn.rs +++ b/crates/vm/src/warn.rs @@ -365,23 +365,25 @@ fn filter_search( }; let tmp_item = PyTupleRef::try_from_object(vm, tmp_item) .ok() - .filter(|t| t.len() == 5) + .filter(|t| t.as_slice().len() == 5) .ok_or_else(|| { vm.new_value_error(format!("warnings.{list_name} item {i} isn't a 5-tuple")) })?; /* action, msg, cat, mod, ln = item */ - let action = &tmp_item[0]; + let action = &tmp_item.as_slice()[0]; if !action.class().is(vm.ctx.types.str_type) { return Err(vm.new_type_error(format!( "action must be a string, not '{}'", action.class().name() ))); } - let good_msg = check_matched(&tmp_item[1], text, vm)?; - let is_subclass = category.is_subclass(&tmp_item[2], vm)?; - let good_mod = check_matched(&tmp_item[3], module, vm)?; - let ln: usize = tmp_item[4].try_int(vm).map_or(0, |v| v.as_u32_mask() as _); + let good_msg = check_matched(&tmp_item.as_slice()[1], text, vm)?; + let is_subclass = category.is_subclass(&tmp_item.as_slice()[2], vm)?; + let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?; + let ln: usize = tmp_item.as_slice()[4] + .try_int(vm) + .map_or(0, |v| v.as_u32_mask() as _); if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) { return Ok(Some(action.to_owned())); @@ -478,7 +480,7 @@ pub fn warn_with_skip( vm: &VirtualMachine, ) -> PyResult<()> { if let Some(prefixes) = skip_file_prefixes - && !prefixes.is_empty() + && !prefixes.as_slice().is_empty() && stack_level < 2 { stack_level = 2; @@ -676,7 +678,7 @@ fn show_warning( fn is_filename_to_skip(frame: &crate::frame::FrameObject, prefixes: &Py) -> bool { let filename = frame.f_code().co_filename(); let filename_bytes = filename.as_bytes(); - prefixes.iter().any(|prefix| { + prefixes.as_slice().iter().any(|prefix| { prefix .downcast_ref::() .is_some_and(|s| filename_bytes.starts_with(s.as_bytes())) diff --git a/crates/wasm/src/ssl.rs b/crates/wasm/src/ssl.rs index d938c1aa076..b9210a4b75e 100644 --- a/crates/wasm/src/ssl.rs +++ b/crates/wasm/src/ssl.rs @@ -153,7 +153,7 @@ mod _ssl { return strerror.str(vm); } let args = zelf.args(); - if args.len() == 1 { + if args.as_slice().len() == 1 { args.as_slice()[0].str(vm) } else { args.as_object().str(vm) From 2a111ef575497887c89fb2cce830e16090bbbd6b Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 00:06:04 +0900 Subject: [PATCH 02/12] Read PyBytes payloads through as_bytes() - Callers read bytes through Py::as_bytes(). - bytes.rs reaches PyBytesInner and the remaining PyBytes methods through the payload field; fspath and os call payload.contains_nuls(). - DictKey for [u8] falls back to the PyObject key_eq; it previously resolved to its own key_eq through Deref. Assisted-by: Claude Code:claude-opus-5-5 --- crates/stdlib/src/array.rs | 2 +- crates/stdlib/src/ssl.rs | 2 +- crates/stdlib/src/ssl/compat.rs | 2 +- crates/vm/src/builtins/bytes.rs | 32 +++++++++++++++----------- crates/vm/src/codecs.rs | 2 +- crates/vm/src/dict_inner.rs | 2 +- crates/vm/src/function/fspath.rs | 6 ++--- crates/vm/src/ospath.rs | 4 ++-- crates/vm/src/protocol/number.rs | 2 +- crates/vm/src/stdlib/_ctypes/simple.rs | 2 +- crates/vm/src/stdlib/_io.rs | 6 ++--- crates/vm/src/stdlib/os.rs | 2 +- crates/vm/src/vm/mod.rs | 1 + 13 files changed, 35 insertions(+), 30 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index 0f45927adf1..ffc4c3cde9d 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -955,7 +955,7 @@ pub mod array { .downcast::() .map_err(|_| vm.new_type_error("read() didn't return bytes"))?; - let not_enough_bytes = b.len() != n_bytes; + let not_enough_bytes = b.as_bytes().len() != n_bytes; self._from_bytes(b.as_bytes(), itemsize, vm)?; diff --git a/crates/stdlib/src/ssl.rs b/crates/stdlib/src/ssl.rs index a8a3c08d6a1..932f087577b 100644 --- a/crates/stdlib/src/ssl.rs +++ b/crates/stdlib/src/ssl.rs @@ -2545,7 +2545,7 @@ mod _ssl { e } })?; - if bytes.is_empty() { + if bytes.as_bytes().is_empty() { return Err( if self.is_bio_mode() && !self.io.incoming().as_ref().is_some_and(|bio| bio.eof()) diff --git a/crates/stdlib/src/ssl/compat.rs b/crates/stdlib/src/ssl/compat.rs index d6c183cfc01..09c180bcaab 100644 --- a/crates/stdlib/src/ssl/compat.rs +++ b/crates/stdlib/src/ssl/compat.rs @@ -427,7 +427,7 @@ pub(super) fn recv_at_most_one_tls_record( } })?; socket.observe_tls(false, bytes.as_bytes(), vm); - if bytes.is_empty() { + if bytes.as_bytes().is_empty() { Err(if socket.is_bio_mode() && !socket.transport_eof() { SslError::WantRead } else { diff --git a/crates/vm/src/builtins/bytes.rs b/crates/vm/src/builtins/bytes.rs index 89235de9527..6a892e99f0f 100644 --- a/crates/vm/src/builtins/bytes.rs +++ b/crates/vm/src/builtins/bytes.rs @@ -192,7 +192,8 @@ impl PyRef { // This only works for `bytes` itself, not its subclasses. return Ok(self); } - self.inner + self.payload + .inner .mul(count, vm) .map(|x| PyBytes::from(x).into_ref(&vm.ctx)) } @@ -243,7 +244,7 @@ impl PyBytes { fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { let zelf = zelf.downcast_ref::().expect("expected bytes"); PyBytesInner::warn_on_str("str() on a bytes instance", vm)?; - Ok(vm.ctx.new_str(zelf.inner.repr_bytes(vm)?)) + Ok(vm.ctx.new_str(zelf.payload.inner.repr_bytes(vm)?)) } fn __add__(&self, other: ArgBytesLike) -> Vec { @@ -566,7 +567,7 @@ impl Py { #[pymethod] fn __reduce__(&self, vm: &VirtualMachine) -> (PyTypeRef, PyTupleRef, Option) { - let bytes = PyBytes::from(self.to_vec()).to_pyobject(vm); + let bytes = PyBytes::from(self.as_bytes().to_vec()).to_pyobject(vm); ( self.class().to_owned(), PyTuple::new_ref(vec![bytes], &vm.ctx), @@ -582,13 +583,13 @@ impl PyRef { if self.is(vm.ctx.types.bytes_type) { self } else { - PyBytes::from(self.inner.clone()).into_ref(&vm.ctx) + PyBytes::from(self.payload.inner.clone()).into_ref(&vm.ctx) } } #[pymethod] fn lstrip(self, options: ByteInnerStripOptions, vm: &VirtualMachine) -> Self { - let stripped = self.inner.lstrip(options.bytes); + let stripped = self.payload.inner.lstrip(options.bytes); if stripped == self.as_bytes() { self } else { @@ -598,7 +599,7 @@ impl PyRef { #[pymethod] fn rstrip(self, options: ByteInnerStripOptions, vm: &VirtualMachine) -> Self { - let stripped = self.inner.rstrip(options.bytes); + let stripped = self.payload.inner.rstrip(options.bytes); if stripped == self.as_bytes() { self } else { @@ -655,9 +656,9 @@ impl AsMapping for PyBytes { length: atomic_func!(|mapping, _vm| { Ok(PyBytes::mapping_downcast(mapping).as_bytes().len()) }), - subscript: atomic_func!( - |mapping, needle, vm| PyBytes::mapping_downcast(mapping)._getitem(needle, vm) - ), + subscript: atomic_func!(|mapping, needle, vm| PyBytes::mapping_downcast(mapping) + .payload + ._getitem(needle, vm)), ..PyMappingMethods::NOT_IMPLEMENTED }); &AS_MAPPING @@ -670,6 +671,7 @@ impl AsSequence for PyBytes { length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).as_bytes().len())), concat: atomic_func!(|seq, other, vm| { PyBytes::sequence_downcast(seq) + .payload .inner .concat(other, vm) .map(|x| vm.ctx.new_bytes(x).into()) @@ -687,7 +689,9 @@ impl AsSequence for PyBytes { .map(|x| vm.ctx.new_bytes(vec![x]).into()) }), contains: atomic_func!(|seq, other, vm| { - PyBytes::sequence_downcast(seq).__contains__(other.to_owned(), vm) + PyBytes::sequence_downcast(seq) + .payload + .__contains__(other.to_owned(), vm) }), ..PySequenceMethods::NOT_IMPLEMENTED }); @@ -700,7 +704,7 @@ impl AsNumber for PyBytes { static AS_NUMBER: PyNumberMethods = PyNumberMethods { remainder: Some(|a, b, vm| { if let Some(a) = a.downcast_ref::() { - a.__mod__(b.to_owned(), vm).to_pyresult(vm) + a.payload().__mod__(b.to_owned(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } @@ -714,7 +718,7 @@ impl AsNumber for PyBytes { impl Hashable for PyBytes { #[inline] fn hash(zelf: &Py, vm: &VirtualMachine) -> PyResult { - Ok(zelf.inner.hash(vm)) + Ok(zelf.payload.inner.hash(vm)) } } @@ -738,7 +742,7 @@ impl Comparable for PyBytes { other.class().slot_name() ))); } else { - zelf.inner.cmp(other, op, vm) + zelf.payload.inner.cmp(other, op, vm) }) } } @@ -755,7 +759,7 @@ impl Iterable for PyBytes { impl Representable for PyBytes { #[inline] fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { - zelf.inner.repr_bytes(vm) + zelf.payload.inner.repr_bytes(vm) } } diff --git a/crates/vm/src/codecs.rs b/crates/vm/src/codecs.rs index 0be77b8f575..5fba990feea 100644 --- a/crates/vm/src/codecs.rs +++ b/crates/vm/src/codecs.rs @@ -911,7 +911,7 @@ impl Deref for PyDecodeData<'_> { fn deref(&self) -> &Self::Target { match self { PyDecodeData::Original(data) => data, - PyDecodeData::Modified(data) => data, + PyDecodeData::Modified(data) => data.as_bytes(), } } } diff --git a/crates/vm/src/dict_inner.rs b/crates/vm/src/dict_inner.rs index d323ec2b982..71c62cff04b 100644 --- a/crates/vm/src/dict_inner.rs +++ b/crates/vm/src/dict_inner.rs @@ -1650,7 +1650,7 @@ impl DictKey for [u8] { } else { // Fall back to PyObjectRef implementation. let s = vm.ctx.new_bytes(self.to_vec()); - s.key_eq(vm, other_key) + s.as_object().key_eq(vm, other_key) } } diff --git a/crates/vm/src/function/fspath.rs b/crates/vm/src/function/fspath.rs index f0ab2909059..671ef47d8d9 100644 --- a/crates/vm/src/function/fspath.rs +++ b/crates/vm/src/function/fspath.rs @@ -46,7 +46,7 @@ impl FsPath { Self::Str(s) } b @ PyBytes => { - if check_for_nul && b.contains_nuls() { + if check_for_nul && b.payload.contains_nuls() { cold_path(); return Err(crate::exceptions::nul_char_error(vm)); } @@ -101,14 +101,14 @@ impl FsPath { pub fn to_string_lossy(&self) -> Cow<'_, str> { match self { Self::Str(s) => s.to_string_lossy(), - Self::Bytes(s) => String::from_utf8_lossy(s), + Self::Bytes(s) => String::from_utf8_lossy(s.as_bytes()), } } pub fn to_path_buf(&self, vm: &VirtualMachine) -> PyResult { let path = match self { Self::Str(s) => PathBuf::from(vm.fsencode(s)?.as_ref() as &OsStr), - Self::Bytes(b) => PathBuf::from(Self::bytes_as_os_str(b, vm)?), + Self::Bytes(b) => PathBuf::from(Self::bytes_as_os_str(b.as_bytes(), vm)?), }; Ok(path) } diff --git a/crates/vm/src/ospath.rs b/crates/vm/src/ospath.rs index efe9141da36..9bf17f183f9 100644 --- a/crates/vm/src/ospath.rs +++ b/crates/vm/src/ospath.rs @@ -140,8 +140,8 @@ impl PathConverter { })) } b @ PyBytes => { - check_nul(&b)?; - let path = FsPath::bytes_as_os_str(&b, vm)?.to_owned(); + check_nul(b.as_bytes())?; + let path = FsPath::bytes_as_os_str(b.as_bytes(), vm)?.to_owned(); Ok(Ok(OsPath { path, origin: Some(b.into()), diff --git a/crates/vm/src/protocol/number.rs b/crates/vm/src/protocol/number.rs index f3bd82da7a5..db14a08d4eb 100644 --- a/crates/vm/src/protocol/number.rs +++ b/crates/vm/src/protocol/number.rs @@ -84,7 +84,7 @@ impl PyObject { } else if let Some(s) = self.downcast_ref::() { try_convert(self, numeric_literal_from_str(s).as_bytes(), vm) } else if let Some(bytes) = self.downcast_ref::() { - try_convert(self, bytes, vm) + try_convert(self, bytes.as_bytes(), vm) } else if let Some(bytearray) = self.downcast_ref::() { try_convert(self, &bytearray.borrow_buf(), vm) } else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self) { diff --git a/crates/vm/src/stdlib/_ctypes/simple.rs b/crates/vm/src/stdlib/_ctypes/simple.rs index e6e54e4f66f..a78d6db84df 100644 --- a/crates/vm/src/stdlib/_ctypes/simple.rs +++ b/crates/vm/src/stdlib/_ctypes/simple.rs @@ -734,7 +734,7 @@ fn value_to_bytes_endian( "c" => { // c_char - single byte (bytes, bytearray, or int 0-255) if let Some(bytes) = value.downcast_ref::() - && !bytes.is_empty() + && !bytes.as_bytes().is_empty() { SimpleStorageValue::Byte(bytes.as_bytes()[0]) } else if let Some(bytearray) = value.downcast_ref::() { diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index 42c6654dd76..7e264d17aa9 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -3606,7 +3606,7 @@ mod _io { }; let pos = Offset::try_from_object(vm, pos)?; let mut cookie = TextIOCookie { - start_pos: pos - next_input.len() as Offset, + start_pos: pos - next_input.as_bytes().len() as Offset, dec_flags: *dec_flags, ..Default::default() }; @@ -3632,7 +3632,7 @@ mod _io { let n_decoded = decoder_decode(input)?; if n_decoded.chars <= num_to_skip.chars { let (dec_buffer, dec_flags) = decoder_getstate()?; - if dec_buffer.is_empty() { + if dec_buffer.as_bytes().is_empty() { cookie.dec_flags = dec_flags; num_to_skip -= n_decoded; break; @@ -3663,7 +3663,7 @@ mod _io { n_decoded += n; cookie.bytes_to_feed += 1; let (dec_buffer, dec_flags) = decoder_getstate()?; - if dec_buffer.is_empty() && n_decoded.chars <= num_to_skip.chars { + if dec_buffer.as_bytes().is_empty() && n_decoded.chars <= num_to_skip.chars { cookie.start_pos += cookie.bytes_to_feed as Offset; num_to_skip -= n_decoded; cookie.dec_flags = dec_flags; diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 85ea738f636..f2b9e861bda 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -532,7 +532,7 @@ pub(super) mod _os { ) -> Option<&[u8]> { match obj { crate::function::Either::A(s) if !s.contains_nuls() => Some(s.as_bytes()), - crate::function::Either::B(b) if !b.contains_nuls() => Some(b.as_bytes()), + crate::function::Either::B(b) if !b.payload.contains_nuls() => Some(b.as_bytes()), _ => { cold_path(); None diff --git a/crates/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs index ae986145c7c..85f87ab5b20 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -3839,6 +3839,7 @@ impl VirtualMachine { .state .codec_registry .encode_text(s.to_owned(), "utf-8", Some(errors), self)? + .as_bytes() .to_vec(); // XXX: this is sketchy on windows; it's not guaranteed that the // OsStr encoding will always be compatible with WTF-8. From 722b0345c155e42f4bba907a755b534d1b7a302d Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 00:52:35 +0900 Subject: [PATCH 03/12] Copy capi tuple arguments with as_slice().to_vec() Assisted-by: Claude Code:claude-opus-5-5 --- crates/capi/src/abstract_.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/capi/src/abstract_.rs b/crates/capi/src/abstract_.rs index 8447d502363..28ff80e50dc 100644 --- a/crates/capi/src/abstract_.rs +++ b/crates/capi/src/abstract_.rs @@ -19,7 +19,7 @@ mod sequence; const PY_VECTORCALL_ARGUMENTS_OFFSET: usize = 1usize << (usize::BITS as usize - 1); fn tuple_to_args(tuple: &Py) -> PosArgs { - tuple.as_slice().iter().cloned().collect::>().into() + tuple.as_slice().to_vec().into() } fn dict_to_kwargs(vm: &VirtualMachine, dict: &Py) -> PyResult { @@ -150,7 +150,7 @@ pub unsafe extern "C" fn PyVectorcall_Call( let callable = unsafe { callable.assume_borrowed() }; let tuple = unsafe { tuple.assume_borrowed_and_cast::(vm) }?; - let mut args = tuple.as_slice().iter().cloned().collect::>(); + let mut args = tuple.as_slice().to_vec(); let num_positional_args = args.len(); let mut kwnames = Vec::new(); From 89c713fb42bedf12b9f7b0cc8ced162207b50175 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 01:27:18 +0900 Subject: [PATCH 04/12] Read sqlite3 Row tuples through as_slice() Assisted-by: Claude Code:claude-opus-5-5 --- crates/stdlib/src/_sqlite3.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/stdlib/src/_sqlite3.rs b/crates/stdlib/src/_sqlite3.rs index 5e7a90dc847..309c76c6f1c 100644 --- a/crates/stdlib/src/_sqlite3.rs +++ b/crates/stdlib/src/_sqlite3.rs @@ -2372,6 +2372,7 @@ mod _sqlite3 { #[pymethod] fn keys(&self, _vm: &VirtualMachine) -> Vec { self.description + .as_slice() .iter() .map(|x| x.downcast_ref::().unwrap().as_slice()[0].clone()) .collect() @@ -2380,9 +2381,9 @@ mod _sqlite3 { fn subscript(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { if let Some(i) = needle.downcast_ref::() { let i = i.try_to_primitive::(vm)?; - self.data.getitem_by_index(vm, i) + self.data.as_slice().getitem_by_index(vm, i) } else if let Some(name) = needle.downcast_ref::() { - for (obj, i) in self.description.iter().zip(0..) { + for (obj, i) in self.description.as_slice().iter().zip(0..) { let obj = &obj.downcast_ref::().unwrap().as_slice()[0]; let Some(obj) = obj.downcast_ref::() else { break; @@ -2391,12 +2392,15 @@ mod _sqlite3 { let b_iter = obj.expect_str().chars().flat_map(|x| x.to_uppercase()); if a_iter.eq(b_iter) { - return self.data.getitem_by_index(vm, i); + return self.data.as_slice().getitem_by_index(vm, i); } } Err(vm.new_index_error(format!("No item with key '{}'", name.to_string_lossy()))) } else if let Some(slice) = needle.downcast_ref::() { - let list = self.data.getitem_by_slice(vm, slice.to_saturated(vm)?)?; + let list = self + .data + .as_slice() + .getitem_by_slice(vm, slice.to_saturated(vm)?)?; Ok(vm.ctx.new_tuple(list).into()) } else { Err(vm.new_index_error("Index must be int or string")) @@ -2460,6 +2464,7 @@ mod _sqlite3 { std::sync::LazyLock::new(|| PyMappingMethods { length: atomic_func!(|mapping, _vm| Ok(Row::mapping_downcast(mapping) .data + .as_slice() .len())), subscript: atomic_func!(|mapping, needle, vm| { Row::mapping_downcast(mapping).subscript(needle, vm) @@ -2474,9 +2479,13 @@ mod _sqlite3 { fn as_sequence() -> &'static PySequenceMethods { static AS_SEQUENCE: std::sync::LazyLock = std::sync::LazyLock::new(|| PySequenceMethods { - length: atomic_func!(|seq, _vm| Ok(Row::sequence_downcast(seq).data.len())), + length: atomic_func!(|seq, _vm| Ok(Row::sequence_downcast(seq) + .data + .as_slice() + .len())), item: atomic_func!(|seq, i, vm| Row::sequence_downcast(seq) .data + .as_slice() .getitem_by_index(vm, i)), ..PySequenceMethods::NOT_IMPLEMENTED }); From febea26405b2cb79a826077de2adb5d64bdacb25 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 01:53:00 +0900 Subject: [PATCH 05/12] Read platform-specific tuples and bytes through as_slice() and as_bytes() Assisted-by: Claude Code:claude-opus-5-5 --- crates/stdlib/src/overlapped.rs | 14 ++--- crates/stdlib/src/socket.rs | 94 ++++++++++++++++++++------------- crates/vm/src/stdlib/_codecs.rs | 2 +- crates/vm/src/stdlib/posix.rs | 12 ++--- 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/crates/stdlib/src/overlapped.rs b/crates/stdlib/src/overlapped.rs index 19de59a6188..65f7aa71c89 100644 --- a/crates/stdlib/src/overlapped.rs +++ b/crates/stdlib/src/overlapped.rs @@ -208,11 +208,11 @@ mod _overlapped { /// Parse a Python address tuple to SOCKADDR fn parse_address(addr_obj: &Py, vm: &VirtualMachine) -> PyResult<(Vec, i32)> { - match addr_obj.len() { + match addr_obj.as_slice().len() { 2 => { // IPv4: (host, port) - let host: PyStrRef = addr_obj[0].clone().try_into_value(vm)?; - let port: u16 = addr_obj[1].clone().try_to_value(vm)?; + let host: PyStrRef = addr_obj.as_slice()[0].clone().try_into_value(vm)?; + let port: u16 = addr_obj.as_slice()[1].clone().try_to_value(vm)?; let host_wide = host .as_wtf8() .to_wide_cstring() @@ -222,10 +222,10 @@ mod _overlapped { } 4 => { // IPv6: (host, port, flowinfo, scope_id) - let host: PyStrRef = addr_obj[0].clone().try_into_value(vm)?; - let port: u16 = addr_obj[1].clone().try_to_value(vm)?; - let flowinfo: u32 = addr_obj[2].clone().try_to_value(vm)?; - let scope_id: u32 = addr_obj[3].clone().try_to_value(vm)?; + let host: PyStrRef = addr_obj.as_slice()[0].clone().try_into_value(vm)?; + let port: u16 = addr_obj.as_slice()[1].clone().try_to_value(vm)?; + let flowinfo: u32 = addr_obj.as_slice()[2].clone().try_to_value(vm)?; + let scope_id: u32 = addr_obj.as_slice()[3].clone().try_to_value(vm)?; let host_wide = host .as_wtf8() .to_wide_cstring() diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 9b0c8674222..d0a44468183 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -1352,23 +1352,24 @@ mod _socket { obj.class().name() )) })?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm .new_type_error( "AF_HYPERV address must be a str tuple (vm_id, service_id)", ) .into()); } - let vm_id: PyStrRef = tuple[0].clone().downcast().map_err(|_| { - vm.new_type_error( - "AF_HYPERV address must be a str tuple (vm_id, service_id)", - ) - })?; - let service_id: PyStrRef = tuple[1].clone().downcast().map_err(|_| { + let vm_id: PyStrRef = tuple.as_slice()[0].clone().downcast().map_err(|_| { vm.new_type_error( "AF_HYPERV address must be a str tuple (vm_id, service_id)", ) })?; + let service_id: PyStrRef = + tuple.as_slice()[1].clone().downcast().map_err(|_| { + vm.new_type_error( + "AF_HYPERV address must be a str tuple (vm_id, service_id)", + ) + })?; let vm_wide = vm_id.as_wtf8().to_wide_cstring().map_err(|_| { vm.new_value_error(format!( "{caller}(): AF_HYPERV address vm_id is not a valid UUID string" @@ -1410,14 +1411,14 @@ mod _socket { let tuple: PyTupleRef = addr .downcast() .map_err(|_| vm.new_os_error(format!("{caller}(): wrong format")))?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_os_error(format!("{caller}(): wrong format")).into()); } - let name: PyStrRef = tuple[0] + let name: PyStrRef = tuple.as_slice()[0] .clone() .downcast() .map_err(|_| vm.new_os_error(format!("{caller}(): wrong format")))?; - let channel = i64::try_from_object(vm, tuple[1].clone()) + let channel = i64::try_from_object(vm, tuple.as_slice()[1].clone()) .map_err(|_| vm.new_os_error(format!("{caller}(): wrong format")))?; let name = name .try_into_utf8(vm) @@ -1449,6 +1450,7 @@ mod _socket { })?; let proto = self.proto.load(); let interface: PyStrRef = tuple + .as_slice() .first() .cloned() .ok_or_else(|| { @@ -1484,38 +1486,48 @@ mod _socket { match proto { c::CAN_RAW | c::CAN_BCM => { - if tuple.len() != 1 { + if tuple.as_slice().len() != 1 { return Err(vm .new_type_error("AF_CAN address must be a tuple (interface, )") .into()); } } c::CAN_ISOTP => { - if tuple.len() != 3 { + if tuple.as_slice().len() != 3 { return Err(vm .new_type_error( "AF_CAN ISOTP address must be a tuple (interface, rx_id, tx_id)", ) .into()); } - let rx_id = tuple[1].try_index(vm)?.try_to_primitive::(vm)?; - let tx_id = tuple[2].try_index(vm)?.try_to_primitive::(vm)?; + let rx_id = tuple.as_slice()[1] + .try_index(vm)? + .try_to_primitive::(vm)?; + let tx_id = tuple.as_slice()[2] + .try_index(vm)? + .try_to_primitive::(vm)?; unsafe { (*can_addr).can_addr.tp.rx_id = rx_id; (*can_addr).can_addr.tp.tx_id = tx_id; } } c::CAN_J1939 => { - if tuple.len() != 4 { + if tuple.as_slice().len() != 4 { return Err(vm .new_type_error( "AF_CAN J1939 address must be a tuple (interface, name, pgn, addr)", ) .into()); } - let name = tuple[1].try_index(vm)?.try_to_primitive::(vm)?; - let pgn = tuple[2].try_index(vm)?.try_to_primitive::(vm)?; - let jaddr = tuple[3].try_index(vm)?.try_to_primitive::(vm)?; + let name = tuple.as_slice()[1] + .try_index(vm)? + .try_to_primitive::(vm)?; + let pgn = tuple.as_slice()[2] + .try_index(vm)? + .try_to_primitive::(vm)?; + let jaddr = tuple.as_slice()[3] + .try_index(vm)? + .try_to_primitive::(vm)?; unsafe { (*can_addr).can_addr.j1939.name = name; (*can_addr).can_addr.j1939.pgn = pgn; @@ -1547,25 +1559,27 @@ mod _socket { obj.class().name() )) })?; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm .new_type_error("AF_ALG address must be a tuple (type, name)") .into()); } - let alg_type: PyStrRef = tuple[0].clone().downcast().map_err(|obj| { - vm.new_type_error(format!( - "{}(): AF_ALG type must be str, not {}", - caller, - obj.class().name() - )) - })?; - let alg_name: PyStrRef = tuple[1].clone().downcast().map_err(|obj| { - vm.new_type_error(format!( - "{}(): AF_ALG name must be str, not {}", - caller, - obj.class().name() - )) - })?; + let alg_type: PyStrRef = + tuple.as_slice()[0].clone().downcast().map_err(|obj| { + vm.new_type_error(format!( + "{}(): AF_ALG type must be str, not {}", + caller, + obj.class().name() + )) + })?; + let alg_name: PyStrRef = + tuple.as_slice()[1].clone().downcast().map_err(|obj| { + vm.new_type_error(format!( + "{}(): AF_ALG name must be str, not {}", + caller, + obj.class().name() + )) + })?; let alg_type = alg_type.try_into_utf8(vm).map_err(IoOrPyException::from)?; let alg_name = alg_name.try_into_utf8(vm).map_err(IoOrPyException::from)?; @@ -2459,7 +2473,7 @@ mod _socket { let tuple: PyTupleRef = option .downcast() .map_err(|_| vm.new_type_error("SIO_KEEPALIVE_VALS requires a tuple"))?; - if tuple.len() != 3 { + if tuple.as_slice().len() != 3 { return Err(vm .new_type_error( "SIO_KEEPALIVE_VALS requires (onoff, keepalivetime, keepaliveinterval)", @@ -2468,9 +2482,15 @@ mod _socket { } let ka = host_socket::TcpKeepalive { - onoff: TryFromObject::try_from_object(vm, tuple[0].clone())?, - keepalivetime: TryFromObject::try_from_object(vm, tuple[1].clone())?, - keepaliveinterval: TryFromObject::try_from_object(vm, tuple[2].clone())?, + onoff: TryFromObject::try_from_object(vm, tuple.as_slice()[0].clone())?, + keepalivetime: TryFromObject::try_from_object( + vm, + tuple.as_slice()[1].clone(), + )?, + keepaliveinterval: TryFromObject::try_from_object( + vm, + tuple.as_slice()[2].clone(), + )?, }; if cmd != c::SIO_KEEPALIVE_VALS { diff --git a/crates/vm/src/stdlib/_codecs.rs b/crates/vm/src/stdlib/_codecs.rs index 81c502d02ee..7bb6b7d5e09 100644 --- a/crates/vm/src/stdlib/_codecs.rs +++ b/crates/vm/src/stdlib/_codecs.rs @@ -1186,7 +1186,7 @@ mod _codecs_windows { let new_pos_obj = tuple_slice[1].clone(); if let Some(bytes) = replacement.downcast_ref::() { - output.extend_from_slice(bytes); + output.extend_from_slice(bytes.as_bytes()); } else if let Some(rep_str) = replacement.downcast_ref::() { // Replacement string - try to encode each character for rcp in rep_str.as_wtf8().code_points() { diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 276f0c4fc29..cc1e665ee5d 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -1768,15 +1768,15 @@ pub mod module { let Some(tuple) = obj.downcast_ref::() else { return Err(vm.new_type_error("scheduler must be a tuple or None")); }; - if tuple.len() != 2 { + if tuple.as_slice().len() != 2 { return Err(vm.new_type_error("A scheduler tuple must have two elements")); } - let policy = if tuple[0].is(&vm.ctx.none()) { + let policy = if tuple.as_slice()[0].is(&vm.ctx.none()) { None } else { - Some(i32::try_from_object(vm, tuple[0].clone())?) + Some(i32::try_from_object(vm, tuple.as_slice()[0].clone())?) }; - let param = super::posix_sched::convert_sched_param(&tuple[1], vm)?; + let param = super::posix_sched::convert_sched_param(&tuple.as_slice()[1], vm)?; Ok(Some(rustpython_host_env::posix::PosixSpawnScheduler { policy, param, @@ -2901,7 +2901,7 @@ mod posix_sched { |zelf: crate::PyRef, vm: &VirtualMachine| -> PyTupleRef { - vm.new_tuple((zelf.class().to_owned(), (zelf[0].clone(),))) + vm.new_tuple((zelf.class().to_owned(), (zelf.as_slice()[0].clone(),))) }, crate::function::PyMethodFlags::METHOD, None, @@ -2926,7 +2926,7 @@ mod posix_sched { return Err(vm.new_type_error("must have a sched_param object")); } let tuple = obj.downcast_ref::().unwrap(); - let priority = tuple[0].clone(); + let priority = tuple.as_slice()[0].clone(); let priority_type = priority.class().name().to_string(); let value = priority.downcast::().map_err(|_| { vm.new_type_error(format!("an integer is required (got type {priority_type})")) From 07fade3ba2b3008d9f54bc02fbf71471dfbf5a2b Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 10:50:03 +0900 Subject: [PATCH 06/12] Read PyInt and PyFloat payloads through as_bigint() and to_f64() Add `Py::as_bigint()`, `Py::try_to_primitive()` and `Py::to_f64()`, which forward to the payload methods of the same name. Reads of the `value` field and primitive conversions go through these views; other PyInt and PyFloat methods are called through the `payload` field inside rustpython-vm and through payload() outside it. Assisted-by: Claude Code:claude-opus-5-5 --- crates/capi/src/longobject.rs | 6 +-- crates/stdlib/src/array.rs | 2 +- crates/stdlib/src/binascii.rs | 4 +- crates/stdlib/src/fcntl.rs | 2 +- crates/stdlib/src/json.rs | 4 +- crates/stdlib/src/socket.rs | 11 +++-- crates/stdlib/src/zlib.rs | 2 +- crates/vm/src/builtins/bool.rs | 6 +-- crates/vm/src/builtins/float.rs | 34 ++++++++++------ crates/vm/src/builtins/int.rs | 58 +++++++++++++++++---------- crates/vm/src/frame.rs | 4 +- crates/vm/src/protocol/object.rs | 2 +- crates/vm/src/stdlib/_ast/constant.rs | 6 +-- crates/vm/src/stdlib/os.rs | 5 ++- crates/vm/src/warn.rs | 4 +- 15 files changed, 91 insertions(+), 59 deletions(-) diff --git a/crates/capi/src/longobject.rs b/crates/capi/src/longobject.rs index 89c0868a3d1..b00f13d64d5 100644 --- a/crates/capi/src/longobject.rs +++ b/crates/capi/src/longobject.rs @@ -396,9 +396,9 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulon with_vm::, _>(|vm| { let int = unsafe { obj.assume_borrowed() }.to_owned().try_index(vm)?; if const { c_ulong::BITS == 32 } { - Ok(c_ulong::from(int.as_u32_mask())) + Ok(c_ulong::from(int.payload().as_u32_mask())) } else { - Ok(int.as_u64_mask() as c_ulong) + Ok(int.payload().as_u64_mask() as c_ulong) } }) } @@ -407,7 +407,7 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulon pub unsafe extern "C" fn PyLong_AsUnsignedLongLongMask(obj: *mut PyObject) -> c_ulonglong { with_vm::, _>(|vm| { let int = unsafe { obj.assume_borrowed() }.to_owned().try_index(vm)?; - Ok(int.as_u64_mask()) + Ok(int.payload().as_u64_mask()) }) } diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index ffc4c3cde9d..1d349dcb1c7 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -523,7 +523,7 @@ pub mod array { ($($t:ty,)*) => {$( impl ArrayElement for $t { fn try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - obj.try_index(vm)?.try_to_primitive_raw(vm) + obj.try_index(vm)?.payload().try_to_primitive_raw(vm) } fn byteswap(self) -> Self { <$t>::swap_bytes(self) diff --git a/crates/stdlib/src/binascii.rs b/crates/stdlib/src/binascii.rs index 73c20506dd7..31a9079714d 100644 --- a/crates/stdlib/src/binascii.rs +++ b/crates/stdlib/src/binascii.rs @@ -71,7 +71,7 @@ mod decl { } pub(crate) fn crc32(data: ArgBytesLike, crc: ArgIndex) -> u32 { - let crc = crc.into_int_ref().as_u32_mask(); + let crc = crc.into_int_ref().payload().as_u32_mask(); data.with_ref(|bytes| binascii::crc32(bytes, crc)) } @@ -83,7 +83,7 @@ mod decl { #[pyfunction] pub(crate) fn crc_hqx(data: ArgBytesLike, crc: ArgIndex) -> u32 { - data.with_ref(|bytes| binascii::crc_hqx(bytes, crc.into_int_ref().as_u32_mask())) + data.with_ref(|bytes| binascii::crc_hqx(bytes, crc.into_int_ref().payload().as_u32_mask())) } #[derive(FromArgs)] diff --git a/crates/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs index a6dfdeb63b1..76d14f0caa2 100644 --- a/crates/stdlib/src/fcntl.rs +++ b/crates/stdlib/src/fcntl.rs @@ -114,7 +114,7 @@ mod fcntl { )?; return Ok(vm.ctx.new_bytes(buf[..arg_len].to_vec()).into()); } - Either::B(i) => i.as_u32_mask(), + Either::B(i) => i.payload().as_u32_mask(), }; let ret = retry_on_eintr( vm, diff --git a/crates/stdlib/src/json.rs b/crates/stdlib/src/json.rs index 7e7e622bff9..4debccaedc7 100644 --- a/crates/stdlib/src/json.rs +++ b/crates/stdlib/src/json.rs @@ -1037,7 +1037,7 @@ mod _json { return Ok(true); } if let Some(i) = key.downcast_ref::() { - self.write_text(&Wtf8Buf::from(i.to_str_radix_10()), out, vm)?; + self.write_text(&Wtf8Buf::from(i.payload().to_str_radix_10()), out, vm)?; return Ok(true); } if self.skipkeys { @@ -1267,7 +1267,7 @@ mod _json { out.push_str("false"); Ok(()) } else if let Some(i) = obj.downcast_ref::() { - out.push_str(&i.to_str_radix_10()); + out.push_str(&i.payload().to_str_radix_10()); Ok(()) } else if let Some(f) = obj.downcast_ref::() { let text = self.float_repr(f.to_f64(), vm)?; diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index d0a44468183..35c71284cc0 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2571,7 +2571,12 @@ mod _socket { let addr = Self::from_tuple(tuple, vm)?; let flowinfo = tuple .get(2) - .map(|obj| obj.clone().try_index(vm)?.try_to_primitive_raw(vm)) + .map(|obj| { + obj.clone() + .try_index(vm)? + .payload() + .try_to_primitive_raw(vm) + }) .transpose()? .unwrap_or(0); let scopeid = tuple @@ -3471,7 +3476,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_LEN")] fn cmsg_len(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.payload().try_to_primitive_raw(vm)?; host_socket::checked_cmsg_len(length) .ok_or_else(|| vm.new_overflow_error("CMSG_LEN() argument out of range")) } @@ -3479,7 +3484,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_SPACE")] fn cmsg_space(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.payload().try_to_primitive_raw(vm)?; host_socket::checked_cmsg_space(length) .ok_or_else(|| vm.new_overflow_error("CMSG_SPACE() argument out of range")) } diff --git a/crates/stdlib/src/zlib.rs b/crates/stdlib/src/zlib.rs index d068b02a642..3e9e1034486 100644 --- a/crates/stdlib/src/zlib.rs +++ b/crates/stdlib/src/zlib.rs @@ -61,7 +61,7 @@ mod zlib { fn adler32(args: Adler32Args) -> u32 { let Adler32Args { data, value } = args; data.with_ref(|data| { - let value = value.into_int_ref().as_u32_mask(); + let value = value.into_int_ref().payload().as_u32_mask(); let mut hasher = Adler32::from_value(value); hasher.update_buffer(data); hasher.hash() diff --git a/crates/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs index a8c2c019582..269dfed6110 100644 --- a/crates/vm/src/builtins/bool.rs +++ b/crates/vm/src/builtins/bool.rs @@ -139,7 +139,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs || rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.__or__(rhs).to_pyobject(vm) + lhs.payload.__or__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } @@ -153,7 +153,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs && rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.__and__(rhs).to_pyobject(vm) + lhs.payload.__and__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } @@ -167,7 +167,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs ^ rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.__xor__(rhs).to_pyobject(vm) + lhs.payload.__xor__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } diff --git a/crates/vm/src/builtins/float.rs b/crates/vm/src/builtins/float.rs index 807da8804e1..4b11db274f2 100644 --- a/crates/vm/src/builtins/float.rs +++ b/crates/vm/src/builtins/float.rs @@ -33,6 +33,14 @@ impl PyFloat { } } +impl Py { + #[must_use] + #[inline] + pub const fn to_f64(&self) -> f64 { + self.payload.to_f64() + } +} + thread_local! { static FLOAT_FREELIST: Cell> = const { Cell::new(crate::object::FreeList::new()) }; } @@ -97,7 +105,7 @@ impl From for PyFloat { pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult> { let v = if let Some(float) = obj.downcast_ref::() { - Some(float.value) + Some(float.to_f64()) } else if let Some(int) = obj.downcast_ref::() { Some(try_bigint_to_f64(int.as_bigint(), vm)?) } else { @@ -224,7 +232,7 @@ impl Constructor for PyFloat { OptionalArg::Missing => 0.0, OptionalArg::Present(val) => { if let Some(f) = val.try_float_opt(vm) { - f?.value + f?.to_f64() } else { float_from_string(&val, vm)? } @@ -291,9 +299,9 @@ impl PyFloat { FormatSpec::parse(format_spec.as_str()).map_err(|err| err.into_pyexception(vm))?; let result = if format_spec.has_locale_format() { let locale = crate::format::get_locale_info(); - format_spec.format_float_locale(zelf.value, &locale) + format_spec.format_float_locale(zelf.to_f64(), &locale) } else { - format_spec.format_float(zelf.value) + format_spec.format_float(zelf.to_f64()) }; result .map(Wtf8Buf::from_string) @@ -449,8 +457,8 @@ impl Comparable for PyFloat { _vm: &VirtualMachine, ) -> PyResult { let ret = if let Some(other) = other.downcast_ref::() { - zelf.value - .partial_cmp(&other.value) + zelf.to_f64() + .partial_cmp(&other.to_f64()) .map_or_else(|| op == PyComparisonOp::Ne, |ord| op.eval_ord(ord)) } else if let Some(other) = other.downcast_ref::() { let a = zelf.to_f64(); @@ -507,17 +515,17 @@ impl AsNumber for PyFloat { } }), negative: Some(|num, vm| { - let value = PyFloat::number_downcast(num).value; + let value = PyFloat::number_downcast(num).to_f64(); (-value).to_pyresult(vm) }), positive: Some(|num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)), absolute: Some(|num, vm| { - let value = PyFloat::number_downcast(num).value; + let value = PyFloat::number_downcast(num).to_f64(); value.abs().to_pyresult(vm) }), - boolean: Some(|num, _vm| Ok(!PyFloat::number_downcast(num).value.is_zero())), + boolean: Some(|num, _vm| Ok(!PyFloat::number_downcast(num).to_f64().is_zero())), int: Some(|num, vm| { - let value = PyFloat::number_downcast(num).value; + let value = PyFloat::number_downcast(num).to_f64(); try_to_bigint(value, vm).map(|x| PyInt::from(x).into_pyobject(vm)) }), float: Some(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm).into())), @@ -530,14 +538,14 @@ impl AsNumber for PyFloat { #[inline] fn clone_exact(zelf: &Py, vm: &VirtualMachine) -> PyRef { - vm.ctx.new_float(zelf.value) + vm.ctx.new_float(zelf.to_f64()) } } impl Representable for PyFloat { #[inline] fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { - Ok(crate::literal::float::to_string(zelf.value)) + Ok(crate::literal::float::to_string(zelf.to_f64())) } } @@ -558,7 +566,7 @@ impl PyFloat { // Retrieve inner float value: #[cfg(feature = "serde")] pub(crate) fn get_value(obj: &PyObject) -> f64 { - obj.downcast_ref::().unwrap().value + obj.downcast_ref::().unwrap().to_f64() } fn vectorcall_float( diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 94e0cb39fc5..4e8632a7e9e 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -392,7 +392,7 @@ impl PyInt { { let r = other .downcast_ref::() - .map(|other| op(&self.value, &other.value)); + .map(|other| op(&self.value, other.as_bigint())); PyArithmeticValue::from_option(r) } @@ -402,13 +402,29 @@ impl PyInt { F: Fn(&BigInt, &BigInt) -> PyResult, { if let Some(other) = other.downcast_ref::() { - op(&self.value, &other.value) + op(&self.value, other.as_bigint()) } else { Ok(vm.ctx.not_implemented()) } } } +impl Py { + #[must_use] + #[inline] + pub const fn as_bigint(&self) -> &BigInt { + self.payload.as_bigint() + } + + #[inline] + pub fn try_to_primitive<'a, I>(&'a self, vm: &VirtualMachine) -> PyResult + where + I: PrimInt + TryFrom<&'a BigInt>, + { + self.payload.try_to_primitive(vm) + } +} + #[derive(FromArgs)] struct RoundArgs { #[pyarg(positional, optional)] @@ -487,12 +503,12 @@ impl PyInt { && ndigits > 0 { // Work with positive integers and negate at the end if necessary - let sign = if zelf.value.is_negative() { + let sign = if zelf.as_bigint().is_negative() { BigInt::from(-1) } else { BigInt::from(1) }; - let value = zelf.value.abs(); + let value = zelf.as_bigint().abs(); // Divide and multiply by the power of 10 to get the approximate answer let pow10 = BigInt::from(10).pow(ndigits); @@ -545,13 +561,13 @@ impl PyInt { let format_spec = FormatSpec::parse(format_spec.as_str()).map_err(|err| err.into_pyexception(vm))?; if format_spec.is_decimal_int_format() { - check_int_to_str_digits(&zelf.value, vm)?; + check_int_to_str_digits(zelf.as_bigint(), vm)?; } let result = if format_spec.has_locale_format() { let locale = crate::format::get_locale_info(); - format_spec.format_int_locale(&zelf.value, &locale) + format_spec.format_int_locale(zelf.as_bigint(), &locale) } else { - format_spec.format_int(&zelf.value) + format_spec.format_int(zelf.as_bigint()) }; result .map(Wtf8Buf::from_string) @@ -687,7 +703,7 @@ impl PyRef { pub(crate) fn __int__(self, vm: &VirtualMachine) -> PyRefExact { self.into_exact_or(&vm.ctx, |zelf| unsafe { // TODO: this is actually safe. we need better interface - PyRefExact::new_unchecked(vm.ctx.new_bigint(&zelf.value)) + PyRefExact::new_unchecked(vm.ctx.new_bigint(zelf.as_bigint())) }) } } @@ -701,7 +717,7 @@ impl Comparable for PyInt { ) -> PyResult { let r = other .downcast_ref::() - .map(|other| op.eval_ord(zelf.value.cmp(&other.value))); + .map(|other| op.eval_ord(zelf.as_bigint().cmp(other.as_bigint()))); Ok(PyComparisonValue::from_option(r)) } } @@ -736,8 +752,8 @@ pub(crate) fn check_int_to_str_digits(value: &BigInt, vm: &VirtualMachine) -> Py impl Representable for PyInt { #[inline] fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { - check_int_to_str_digits(&zelf.value, vm)?; - Ok(zelf.to_str_radix_10()) + check_int_to_str_digits(zelf.as_bigint(), vm)?; + Ok(zelf.payload.to_str_radix_10()) } } @@ -756,7 +772,7 @@ impl AsNumber for PyInt { #[inline] fn clone_exact(zelf: &Py, vm: &VirtualMachine) -> PyRef { - vm.ctx.new_bigint(&zelf.value) + vm.ctx.new_bigint(zelf.as_bigint()) } } @@ -770,19 +786,19 @@ impl PyInt { power: Some(|a, b, c, vm| { if let Some(a) = a.downcast_ref::() { if vm.is_none(c) { - a.general_op(b, |a, b| inner_pow(a, b, vm), vm) + a.payload.general_op(b, |a, b| inner_pow(a, b, vm), vm) } else { - a.modpow(b, c, vm) + a.payload.modpow(b, c, vm) } } else { Ok(vm.ctx.not_implemented()) } }), - negative: Some(|num, vm| (&Self::number_downcast(num).value).neg().to_pyresult(vm)), + negative: Some(|num, vm| Self::number_downcast(num).as_bigint().neg().to_pyresult(vm)), positive: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())), - absolute: Some(|num, vm| Self::number_downcast(num).value.abs().to_pyresult(vm)), - boolean: Some(|num, _vm| Ok(!Self::number_downcast(num).value.is_zero())), - invert: Some(|num, vm| (&Self::number_downcast(num).value).not().to_pyresult(vm)), + absolute: Some(|num, vm| Self::number_downcast(num).as_bigint().abs().to_pyresult(vm)), + boolean: Some(|num, _vm| Ok(!Self::number_downcast(num).as_bigint().is_zero())), + invert: Some(|num, vm| Self::number_downcast(num).as_bigint().not().to_pyresult(vm)), lshift: Some(|a, b, vm| Self::number_op(a, b, inner_lshift, vm)), rshift: Some(|a, b, vm| Self::number_op(a, b, inner_rshift, vm)), and: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a & b, vm)), @@ -791,7 +807,7 @@ impl PyInt { int: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())), float: Some(|num, vm| { let zelf = Self::number_downcast(num); - try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x).into()) + try_to_float(zelf.as_bigint(), vm).map(|x| vm.ctx.new_float(x).into()) }), floor_divide: Some(|a, b, vm| Self::number_op(a, b, inner_floordiv, vm)), true_divide: Some(|a, b, vm| Self::number_op(a, b, inner_truediv, vm)), @@ -805,7 +821,7 @@ impl PyInt { R: ToPyResult, { if let (Some(a), Some(b)) = (a.downcast_ref::(), b.downcast_ref::()) { - op(&a.value, &b.value, vm).to_pyresult(vm) + op(a.as_bigint(), b.as_bigint(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } @@ -863,7 +879,7 @@ fn try_int_radix(obj: &PyObject, base: u32, vm: &VirtualMachine) -> PyResult &BigInt { - &obj.downcast_ref::().unwrap().value + obj.downcast_ref::().unwrap().as_bigint() } pub fn try_to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult { diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index c1c53cabbfe..6bb0d93789c 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -2960,7 +2960,7 @@ fn specialization_compact_int_value(i: &Py) -> Option { // _PyLong_IsCompact(): a one-digit PyLong (base 2^30), // i.e. abs(value) <= 2^30 - 1. const CPYTHON_COMPACT_LONG_ABS_MAX: i64 = (1i64 << 30) - 1; - let v = i.try_to_i64_fast()?; + let v = i.payload.try_to_i64_fast()?; if (-CPYTHON_COMPACT_LONG_ABS_MAX..=CPYTHON_COMPACT_LONG_ABS_MAX).contains(&v) { Some(v as isize) } else { @@ -9568,7 +9568,7 @@ impl ExecutingFrame<'_> { checked: fn(i64, i64) -> Option, fallback: impl FnOnce(&BigInt, &BigInt) -> BigInt, ) -> PyObjectRef { - if let (Some(av), Some(bv)) = (a.try_to_i64_fast(), b.try_to_i64_fast()) + if let (Some(av), Some(bv)) = (a.payload.try_to_i64_fast(), b.payload.try_to_i64_fast()) && let Some(result) = checked(av, bv) { return vm.ctx.new_int(result).into(); diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index 472f33c26f5..d7c64ba2eac 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -455,7 +455,7 @@ impl PyObject { let obj = match obj.downcast_exact::(vm) { Ok(int) => { check_int_to_str_digits(int.as_bigint(), vm)?; - return Ok(vm.ctx.new_str(int.to_str_radix_10())); + return Ok(vm.ctx.new_str(int.payload.to_str_radix_10())); } Err(obj) => obj, }; diff --git a/crates/vm/src/stdlib/_ast/constant.rs b/crates/vm/src/stdlib/_ast/constant.rs index 4c46691961d..c752efd20ef 100644 --- a/crates/vm/src/stdlib/_ast/constant.rs +++ b/crates/vm/src/stdlib/_ast/constant.rs @@ -430,8 +430,8 @@ fn constant_data_to_object(vm: &VirtualMachine, constant: ConstantData) -> PyObj ) .unwrap() .into_pyobject(vm), - ConstantData::Float { value } => vm.ctx.new_float(value).into_pyobject(vm), - ConstantData::Complex { value } => vm.ctx.new_complex(value).into_pyobject(vm), + ConstantData::Float { value } => vm.ctx.new_float(value).into(), + ConstantData::Complex { value } => vm.ctx.new_complex(value).into(), ConstantData::Ellipsis => vm.ctx.ellipsis.clone().into(), ConstantData::Code { .. } | ConstantData::Slice { .. } => { unreachable!("ast.Constant values cannot contain code objects or slices") @@ -525,7 +525,7 @@ impl Node for ConstantLiteral { ) .unwrap() .into_pyobject(vm), - Self::Float(value) => vm.ctx.new_float(value).into_pyobject(vm), + Self::Float(value) => vm.ctx.new_float(value).into(), Self::Complex { real, imag } => vm .ctx .new_complex(num_complex::Complex::new(real, imag)) diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index f2b9e861bda..12ce2c24a83 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -2123,7 +2123,10 @@ pub(super) mod _os { #[pyfunction] fn waitstatus_to_exitcode(status: WaitStatusArgs, vm: &VirtualMachine) -> PyResult { let status = status.status; - let status = status.try_index(vm)?.try_to_primitive_raw::(vm)?; + let status = status + .try_index(vm)? + .payload + .try_to_primitive_raw::(vm)?; let exitcode = status >> 8; // ExitProcess() accepts an UINT type: // reject exit code which doesn't fit in an UINT diff --git a/crates/vm/src/warn.rs b/crates/vm/src/warn.rs index 3d8e7cb6fb1..535bfbd0555 100644 --- a/crates/vm/src/warn.rs +++ b/crates/vm/src/warn.rs @@ -287,7 +287,7 @@ fn already_warned( let version_matches = version_obj.as_ref().is_some_and(|v| { v.try_int(vm) - .is_ok_and(|i| i.as_u32_mask() as usize == current_version) + .is_ok_and(|i| i.payload.as_u32_mask() as usize == current_version) }); if version_matches { @@ -383,7 +383,7 @@ fn filter_search( let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?; let ln: usize = tmp_item.as_slice()[4] .try_int(vm) - .map_or(0, |v| v.as_u32_mask() as _); + .map_or(0, |v| v.payload.as_u32_mask() as _); if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) { return Ok(Some(action.to_owned())); From 1616e1220560fc83addfd3c627661382a37f9ad7 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 22:42:50 +0900 Subject: [PATCH 07/12] Read PyComplex payloads through as_complex() Add Py::as_complex() and use it at the call sites that read the payload directly. Assisted-by: Claude Code:claude-opus-5-5 --- crates/capi/src/complexobject.rs | 2 +- crates/vm/src/buffer.rs | 2 +- crates/vm/src/builtins/code.rs | 2 +- crates/vm/src/builtins/complex.rs | 54 ++++++++++++++--------- crates/vm/src/stdlib/_ast/constant.rs | 2 +- crates/vm/src/stdlib/_testinternalcapi.rs | 2 +- crates/vm/src/stdlib/marshal.rs | 4 +- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/crates/capi/src/complexobject.rs b/crates/capi/src/complexobject.rs index 1c53df08ef6..b969d466ff2 100644 --- a/crates/capi/src/complexobject.rs +++ b/crates/capi/src/complexobject.rs @@ -23,7 +23,7 @@ fn try_to_complex(vm: &VirtualMachine, obj: &PyObject) -> PyResult { Err(type_err) } }, - |complex| Ok(complex.to_complex()), + |complex| Ok(complex.as_complex()), ) } diff --git a/crates/vm/src/buffer.rs b/crates/vm/src/buffer.rs index e9c557ad190..957d9c525be 100644 --- a/crates/vm/src/buffer.rs +++ b/crates/vm/src/buffer.rs @@ -789,7 +789,7 @@ macro_rules! make_pack_complex { data: &mut [u8], ) -> Result<(), PackError> { let c = if let Some(value) = arg.downcast_ref::() { - value.to_complex() + value.as_complex() } else { ArgIntoComplex::try_from_object(vm, arg) .map_err(|_| { diff --git a/crates/vm/src/builtins/code.rs b/crates/vm/src/builtins/code.rs index 5731fcb736e..98e6d344a98 100644 --- a/crates/vm/src/builtins/code.rs +++ b/crates/vm/src/builtins/code.rs @@ -216,7 +216,7 @@ fn borrow_obj_constant(obj: &PyObject) -> BorrowedConstant<'_, Literal> { } ref f @ super::float::PyFloat => BorrowedConstant::Float { value: f.to_f64() }, ref c @ super::complex::PyComplex => BorrowedConstant::Complex { - value: c.to_complex() + value: c.as_complex() }, ref s @ super::pystr::PyStr => BorrowedConstant::Str { value: s.as_wtf8() }, ref b @ super::bytes::PyBytes => BorrowedConstant::Bytes { diff --git a/crates/vm/src/builtins/complex.rs b/crates/vm/src/builtins/complex.rs index 86c72d5f64a..48e8cf4b1af 100644 --- a/crates/vm/src/builtins/complex.rs +++ b/crates/vm/src/builtins/complex.rs @@ -26,6 +26,14 @@ pub struct PyComplex { value: Complex64, } +impl Py { + #[must_use] + #[inline] + pub const fn as_complex(&self) -> Complex64 { + self.payload.to_complex() + } +} + // spell-checker:ignore MAXFREELIST thread_local! { static COMPLEX_FREELIST: Cell> = const { Cell::new(crate::object::FreeList::new()) }; @@ -88,7 +96,7 @@ impl PyObjectRef { /// and whether the object was a complex originally or coerced into one pub fn try_complex(&self, vm: &VirtualMachine) -> PyResult> { if let Some(complex) = self.downcast_ref_if_exact::(vm) { - return Ok(Some((complex.value, true))); + return Ok(Some((complex.as_complex(), true))); } if let Some(method) = vm.get_method(self.clone(), identifier!(vm, __complex__)) { let result = method?.call((), vm)?; @@ -108,11 +116,11 @@ impl PyObjectRef { vm, )?; - return Ok(Some((ret.value, true))); + return Ok(Some((ret.as_complex(), true))); } return match result.downcast_ref::() { - Some(complex_obj) => Ok(Some((complex_obj.value, true))), + Some(complex_obj) => Ok(Some((complex_obj.as_complex(), true))), None => Err(vm.new_type_error(format!( "__complex__ returned non-complex (type '{}')", result.class().name() @@ -122,7 +130,7 @@ impl PyObjectRef { // `complex` does not have a `__complex__` by default, so subclasses might not either, // use the actual stored value in this case if let Some(complex) = self.downcast_ref::() { - return Ok(Some((complex.value, true))); + return Ok(Some((complex.as_complex(), true))); } if let Some(float) = self.try_float_opt(vm) { @@ -139,7 +147,7 @@ pub(crate) fn init(context: &'static Context) { fn to_op_complex(value: &PyObject, vm: &VirtualMachine) -> PyResult> { let r = if let Some(complex) = value.downcast_ref::() { - Some(complex.value) + Some(complex.as_complex()) } else { float::to_op_float(value, vm)?.map(|float| Complex64::new(float, 0.0)) }; @@ -513,14 +521,16 @@ impl PyComplex { { let value = match (a.downcast_ref::(), b.downcast_ref::()) { // complex + complex - (Some(a_complex), Some(b_complex)) => cc_op(a_complex.value, b_complex.value), + (Some(a_complex), Some(b_complex)) => { + cc_op(a_complex.as_complex(), b_complex.as_complex()) + } (Some(a_complex), None) => { let Some(b_real) = float::to_op_float(b, vm)? else { return Ok(vm.ctx.not_implemented()); }; // complex + real - cr_op(a_complex.value, b_real) + cr_op(a_complex.as_complex(), b_real) } (None, Some(b_complex)) => { let Some(a_real) = float::to_op_float(a, vm)? else { @@ -528,7 +538,7 @@ impl PyComplex { }; // real + complex - rc_op(a_real, b_complex.value) + rc_op(a_real, b_complex.as_complex()) } (None, None) => return Ok(vm.ctx.not_implemented()), }; @@ -544,13 +554,13 @@ impl PyComplex { #[pymember] fn real(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult { let zelf: &Py = zelf.try_to_value(vm)?; - Ok(vm.ctx.new_float(zelf.value.re).into()) + Ok(vm.ctx.new_float(zelf.as_complex().re).into()) } #[pymember] fn imag(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult { let zelf: &Py = zelf.try_to_value(vm)?; - Ok(vm.ctx.new_float(zelf.value.im).into()) + Ok(vm.ctx.new_float(zelf.as_complex().im).into()) } #[pymethod] @@ -578,9 +588,9 @@ impl PyComplex { FormatSpec::parse(format_spec.as_str()).map_err(|err| err.into_pyexception(vm))?; let result = if format_spec.has_locale_format() { let locale = crate::format::get_locale_info(); - format_spec.format_complex_locale(&zelf.value, &locale) + format_spec.format_complex_locale(&zelf.as_complex(), &locale) } else { - format_spec.format_complex(&zelf.value) + format_spec.format_complex(&zelf.as_complex()) }; result .map(Wtf8Buf::from_string) @@ -617,7 +627,7 @@ impl PyRef { if self.is(vm.ctx.types.complex_type) { self } else { - PyComplex::from(self.value).into_ref(&vm.ctx) + PyComplex::from(self.as_complex()).into_ref(&vm.ctx) } } } @@ -631,10 +641,10 @@ impl Comparable for PyComplex { ) -> PyResult { op.eq_only(|| { let result = if let Some(other) = other.downcast_ref::() { - zelf.value == other.value + zelf.as_complex() == other.as_complex() } else { match float::to_op_float(other, vm) { - Ok(Some(other)) => zelf.value == other.into(), + Ok(Some(other)) => zelf.as_complex() == other.into(), Err(_) => false, Ok(None) => return Ok(PyComparisonValue::NotImplemented), } @@ -647,7 +657,7 @@ impl Comparable for PyComplex { impl Hashable for PyComplex { #[inline] fn hash(zelf: &Py, _vm: &VirtualMachine) -> PyResult { - let value = zelf.value; + let value = zelf.as_complex(); let re_hash = hash::hash_float(value.re).unwrap_or_else(|| hash::hash_object_id(zelf.get_id())); @@ -705,14 +715,14 @@ impl AsNumber for PyComplex { } }), negative: Some(|number, vm| { - let value = PyComplex::number_downcast(number).value; + let value = PyComplex::number_downcast(number).as_complex(); (-value).to_pyresult(vm) }), positive: Some(|number, vm| { PyComplex::number_downcast_exact(number, vm).to_pyresult(vm) }), absolute: Some(|number, vm| { - let value = PyComplex::number_downcast(number).value; + let value = PyComplex::number_downcast(number).as_complex(); let result = value.norm(); // Check for overflow: hypot returns inf for finite inputs that overflow if result.is_infinite() && value.re.is_finite() && value.im.is_finite() { @@ -720,7 +730,9 @@ impl AsNumber for PyComplex { } result.to_pyresult(vm) }), - boolean: Some(|number, _vm| Ok(!PyComplex::number_downcast(number).value.is_zero())), + boolean: Some(|number, _vm| { + Ok(!PyComplex::number_downcast(number).as_complex().is_zero()) + }), true_divide: Some(|a, b, vm| { PyComplex::complex_real_binop( a, @@ -746,7 +758,7 @@ impl AsNumber for PyComplex { } fn clone_exact(zelf: &Py, vm: &VirtualMachine) -> PyRef { - vm.ctx.new_complex(zelf.value) + vm.ctx.new_complex(zelf.as_complex()) } } @@ -755,7 +767,7 @@ impl Representable for PyComplex { fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { // TODO: when you fix this, move it to rustpython_common::complex::repr and update // ast/src/unparse.rs + impl Display for Constant in ast/src/constant.rs - let Complex64 { re, im } = zelf.value; + let Complex64 { re, im } = zelf.as_complex(); Ok(rustpython_literal::complex::to_string(re, im)) } } diff --git a/crates/vm/src/stdlib/_ast/constant.rs b/crates/vm/src/stdlib/_ast/constant.rs index c752efd20ef..61b25f97620 100644 --- a/crates/vm/src/stdlib/_ast/constant.rs +++ b/crates/vm/src/stdlib/_ast/constant.rs @@ -529,7 +529,7 @@ impl Node for ConstantLiteral { Self::Complex { real, imag } => vm .ctx .new_complex(num_complex::Complex::new(real, imag)) - .into_pyobject(vm), + .into(), Self::Ellipsis => vm.ctx.ellipsis.clone().into(), } } diff --git a/crates/vm/src/stdlib/_testinternalcapi.rs b/crates/vm/src/stdlib/_testinternalcapi.rs index 25adf924c22..33d3b172e82 100644 --- a/crates/vm/src/stdlib/_testinternalcapi.rs +++ b/crates/vm/src/stdlib/_testinternalcapi.rs @@ -1756,7 +1756,7 @@ fn py_to_constant_data( } if let Ok(complex) = obj.clone().downcast::() { return Ok(ConstantData::Complex { - value: complex.to_complex(), + value: complex.as_complex(), }); } if obj.class().is(vm.ctx.types.str_type) { diff --git a/crates/vm/src/stdlib/marshal.rs b/crates/vm/src/stdlib/marshal.rs index 2980337c26b..2624e9c0edd 100644 --- a/crates/vm/src/stdlib/marshal.rs +++ b/crates/vm/src/stdlib/marshal.rs @@ -53,7 +53,7 @@ mod decl { f(DumpableValue::Float(pyfloat.to_f64())) } ref pycomplex @ PyComplex => { - f(DumpableValue::Complex(pycomplex.to_complex64())) + f(DumpableValue::Complex(pycomplex.as_complex())) } ref pystr @ PyStr => { f(DumpableValue::Str(pystr.as_wtf8())) @@ -315,7 +315,7 @@ mod decl { write_float_str(buf, f.to_f64()); } } else if let Some(c) = obj.downcast_ref::() { - let cv = c.to_complex64(); + let cv = c.as_complex(); if version > 1 { buf.write_u8(b'y'); buf.write_u64(cv.re.to_bits()); From 89fa92563ceeeed34a0a39112176c1eaede71afa Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 26 Sep 2026 23:52:13 +0900 Subject: [PATCH 08/12] Move PyInt conversion helpers to Py try_to_i64_fast(), to_str_radix_10(), as_u32_mask(), as_u64_mask(), try_to_primitive() and try_to_primitive_raw() only read the integer value, so they are defined on Py and callers no longer go through the payload. PyRange computes its protocol length from the BigInt directly instead of building a PyInt payload. Assisted-by: Claude Code:claude-opus-5-5 --- crates/capi/src/longobject.rs | 6 +-- crates/stdlib/src/array.rs | 2 +- crates/stdlib/src/binascii.rs | 4 +- crates/stdlib/src/fcntl.rs | 2 +- crates/stdlib/src/json.rs | 4 +- crates/stdlib/src/socket.rs | 11 ++--- crates/stdlib/src/zlib.rs | 2 +- crates/vm/src/builtins/int.rs | 82 ++++++++++++++------------------ crates/vm/src/builtins/range.rs | 5 +- crates/vm/src/frame.rs | 4 +- crates/vm/src/protocol/object.rs | 2 +- crates/vm/src/stdlib/os.rs | 5 +- crates/vm/src/warn.rs | 4 +- 13 files changed, 59 insertions(+), 74 deletions(-) diff --git a/crates/capi/src/longobject.rs b/crates/capi/src/longobject.rs index b00f13d64d5..89c0868a3d1 100644 --- a/crates/capi/src/longobject.rs +++ b/crates/capi/src/longobject.rs @@ -396,9 +396,9 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulon with_vm::, _>(|vm| { let int = unsafe { obj.assume_borrowed() }.to_owned().try_index(vm)?; if const { c_ulong::BITS == 32 } { - Ok(c_ulong::from(int.payload().as_u32_mask())) + Ok(c_ulong::from(int.as_u32_mask())) } else { - Ok(int.payload().as_u64_mask() as c_ulong) + Ok(int.as_u64_mask() as c_ulong) } }) } @@ -407,7 +407,7 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulon pub unsafe extern "C" fn PyLong_AsUnsignedLongLongMask(obj: *mut PyObject) -> c_ulonglong { with_vm::, _>(|vm| { let int = unsafe { obj.assume_borrowed() }.to_owned().try_index(vm)?; - Ok(int.payload().as_u64_mask()) + Ok(int.as_u64_mask()) }) } diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index 1d349dcb1c7..ffc4c3cde9d 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -523,7 +523,7 @@ pub mod array { ($($t:ty,)*) => {$( impl ArrayElement for $t { fn try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - obj.try_index(vm)?.payload().try_to_primitive_raw(vm) + obj.try_index(vm)?.try_to_primitive_raw(vm) } fn byteswap(self) -> Self { <$t>::swap_bytes(self) diff --git a/crates/stdlib/src/binascii.rs b/crates/stdlib/src/binascii.rs index 31a9079714d..73c20506dd7 100644 --- a/crates/stdlib/src/binascii.rs +++ b/crates/stdlib/src/binascii.rs @@ -71,7 +71,7 @@ mod decl { } pub(crate) fn crc32(data: ArgBytesLike, crc: ArgIndex) -> u32 { - let crc = crc.into_int_ref().payload().as_u32_mask(); + let crc = crc.into_int_ref().as_u32_mask(); data.with_ref(|bytes| binascii::crc32(bytes, crc)) } @@ -83,7 +83,7 @@ mod decl { #[pyfunction] pub(crate) fn crc_hqx(data: ArgBytesLike, crc: ArgIndex) -> u32 { - data.with_ref(|bytes| binascii::crc_hqx(bytes, crc.into_int_ref().payload().as_u32_mask())) + data.with_ref(|bytes| binascii::crc_hqx(bytes, crc.into_int_ref().as_u32_mask())) } #[derive(FromArgs)] diff --git a/crates/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs index 76d14f0caa2..a6dfdeb63b1 100644 --- a/crates/stdlib/src/fcntl.rs +++ b/crates/stdlib/src/fcntl.rs @@ -114,7 +114,7 @@ mod fcntl { )?; return Ok(vm.ctx.new_bytes(buf[..arg_len].to_vec()).into()); } - Either::B(i) => i.payload().as_u32_mask(), + Either::B(i) => i.as_u32_mask(), }; let ret = retry_on_eintr( vm, diff --git a/crates/stdlib/src/json.rs b/crates/stdlib/src/json.rs index 4debccaedc7..7e7e622bff9 100644 --- a/crates/stdlib/src/json.rs +++ b/crates/stdlib/src/json.rs @@ -1037,7 +1037,7 @@ mod _json { return Ok(true); } if let Some(i) = key.downcast_ref::() { - self.write_text(&Wtf8Buf::from(i.payload().to_str_radix_10()), out, vm)?; + self.write_text(&Wtf8Buf::from(i.to_str_radix_10()), out, vm)?; return Ok(true); } if self.skipkeys { @@ -1267,7 +1267,7 @@ mod _json { out.push_str("false"); Ok(()) } else if let Some(i) = obj.downcast_ref::() { - out.push_str(&i.payload().to_str_radix_10()); + out.push_str(&i.to_str_radix_10()); Ok(()) } else if let Some(f) = obj.downcast_ref::() { let text = self.float_repr(f.to_f64(), vm)?; diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 35c71284cc0..d0a44468183 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2571,12 +2571,7 @@ mod _socket { let addr = Self::from_tuple(tuple, vm)?; let flowinfo = tuple .get(2) - .map(|obj| { - obj.clone() - .try_index(vm)? - .payload() - .try_to_primitive_raw(vm) - }) + .map(|obj| obj.clone().try_index(vm)?.try_to_primitive_raw(vm)) .transpose()? .unwrap_or(0); let scopeid = tuple @@ -3476,7 +3471,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_LEN")] fn cmsg_len(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.payload().try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; host_socket::checked_cmsg_len(length) .ok_or_else(|| vm.new_overflow_error("CMSG_LEN() argument out of range")) } @@ -3484,7 +3479,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_SPACE")] fn cmsg_space(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.payload().try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; host_socket::checked_cmsg_space(length) .ok_or_else(|| vm.new_overflow_error("CMSG_SPACE() argument out of range")) } diff --git a/crates/stdlib/src/zlib.rs b/crates/stdlib/src/zlib.rs index 3e9e1034486..d068b02a642 100644 --- a/crates/stdlib/src/zlib.rs +++ b/crates/stdlib/src/zlib.rs @@ -61,7 +61,7 @@ mod zlib { fn adler32(args: Adler32Args) -> u32 { let Adler32Args { data, value } = args; data.with_ref(|data| { - let value = value.into_int_ref().payload().as_u32_mask(); + let value = value.into_int_ref().as_u32_mask(); let mut hasher = Adler32::from_value(value); hasher.update_buffer(data); hasher.hash() diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 4e8632a7e9e..c9041e39325 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -312,16 +312,47 @@ impl PyInt { &self.value } + #[inline] + fn int_op(&self, other: &PyObject, op: F) -> PyArithmeticValue + where + F: Fn(&BigInt, &BigInt) -> BigInt, + { + let r = other + .downcast_ref::() + .map(|other| op(&self.value, other.as_bigint())); + PyArithmeticValue::from_option(r) + } + + #[inline] + fn general_op(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult + where + F: Fn(&BigInt, &BigInt) -> PyResult, + { + if let Some(other) = other.downcast_ref::() { + op(&self.value, other.as_bigint()) + } else { + Ok(vm.ctx.not_implemented()) + } + } +} + +impl Py { + #[must_use] + #[inline] + pub const fn as_bigint(&self) -> &BigInt { + self.payload.as_bigint() + } + /// Extract the inline magnitude without the generic primitive-conversion path. #[inline(always)] pub(crate) fn try_to_i64_fast(&self) -> Option { - let bits = self.value.bits(); + let bits = self.as_bigint().bits(); if bits > i64::BITS as u64 { return None; } - let magnitude = self.value.iter_u64_digits().next().unwrap_or(0); + let magnitude = self.as_bigint().iter_u64_digits().next().unwrap_or(0); let signed_magnitude = i64::try_from(magnitude).ok(); - match self.value.sign() { + match self.as_bigint().sign() { Sign::Minus if magnitude == 1u64 << 63 => Some(i64::MIN), Sign::Minus => signed_magnitude.map(|value| -value), Sign::NoSign | Sign::Plus => signed_magnitude, @@ -332,9 +363,9 @@ impl PyInt { #[inline] #[must_use] pub fn to_str_radix_10(&self) -> String { - match self.value.to_i64() { + match self.as_bigint().to_i64() { Some(i) => itoa::Buffer::new().format(i).to_owned(), - None => self.value.to_string(), + None => self.as_bigint().to_string(), } } @@ -384,45 +415,6 @@ impl PyInt { )) }) } - - #[inline] - fn int_op(&self, other: &PyObject, op: F) -> PyArithmeticValue - where - F: Fn(&BigInt, &BigInt) -> BigInt, - { - let r = other - .downcast_ref::() - .map(|other| op(&self.value, other.as_bigint())); - PyArithmeticValue::from_option(r) - } - - #[inline] - fn general_op(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult - where - F: Fn(&BigInt, &BigInt) -> PyResult, - { - if let Some(other) = other.downcast_ref::() { - op(&self.value, other.as_bigint()) - } else { - Ok(vm.ctx.not_implemented()) - } - } -} - -impl Py { - #[must_use] - #[inline] - pub const fn as_bigint(&self) -> &BigInt { - self.payload.as_bigint() - } - - #[inline] - pub fn try_to_primitive<'a, I>(&'a self, vm: &VirtualMachine) -> PyResult - where - I: PrimInt + TryFrom<&'a BigInt>, - { - self.payload.try_to_primitive(vm) - } } #[derive(FromArgs)] @@ -753,7 +745,7 @@ impl Representable for PyInt { #[inline] fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { check_int_to_str_digits(zelf.as_bigint(), vm)?; - Ok(zelf.payload.to_str_radix_10()) + Ok(zelf.to_str_radix_10()) } } diff --git a/crates/vm/src/builtins/range.rs b/crates/vm/src/builtins/range.rs index fb893bfc1b4..85ac1f3bdba 100644 --- a/crates/vm/src/builtins/range.rs +++ b/crates/vm/src/builtins/range.rs @@ -424,9 +424,10 @@ impl Py { impl PyRange { fn protocol_length(&self, vm: &VirtualMachine) -> PyResult { - PyInt::from(self.__len__()) - .try_to_primitive::(vm) + self.__len__() + .to_isize() .map(|x| x as usize) + .ok_or_else(|| vm.new_overflow_error("Python int too large to convert to Rust isize")) } } diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 6bb0d93789c..c1c53cabbfe 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -2960,7 +2960,7 @@ fn specialization_compact_int_value(i: &Py) -> Option { // _PyLong_IsCompact(): a one-digit PyLong (base 2^30), // i.e. abs(value) <= 2^30 - 1. const CPYTHON_COMPACT_LONG_ABS_MAX: i64 = (1i64 << 30) - 1; - let v = i.payload.try_to_i64_fast()?; + let v = i.try_to_i64_fast()?; if (-CPYTHON_COMPACT_LONG_ABS_MAX..=CPYTHON_COMPACT_LONG_ABS_MAX).contains(&v) { Some(v as isize) } else { @@ -9568,7 +9568,7 @@ impl ExecutingFrame<'_> { checked: fn(i64, i64) -> Option, fallback: impl FnOnce(&BigInt, &BigInt) -> BigInt, ) -> PyObjectRef { - if let (Some(av), Some(bv)) = (a.payload.try_to_i64_fast(), b.payload.try_to_i64_fast()) + if let (Some(av), Some(bv)) = (a.try_to_i64_fast(), b.try_to_i64_fast()) && let Some(result) = checked(av, bv) { return vm.ctx.new_int(result).into(); diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index d7c64ba2eac..472f33c26f5 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -455,7 +455,7 @@ impl PyObject { let obj = match obj.downcast_exact::(vm) { Ok(int) => { check_int_to_str_digits(int.as_bigint(), vm)?; - return Ok(vm.ctx.new_str(int.payload.to_str_radix_10())); + return Ok(vm.ctx.new_str(int.to_str_radix_10())); } Err(obj) => obj, }; diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 12ce2c24a83..f2b9e861bda 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -2123,10 +2123,7 @@ pub(super) mod _os { #[pyfunction] fn waitstatus_to_exitcode(status: WaitStatusArgs, vm: &VirtualMachine) -> PyResult { let status = status.status; - let status = status - .try_index(vm)? - .payload - .try_to_primitive_raw::(vm)?; + let status = status.try_index(vm)?.try_to_primitive_raw::(vm)?; let exitcode = status >> 8; // ExitProcess() accepts an UINT type: // reject exit code which doesn't fit in an UINT diff --git a/crates/vm/src/warn.rs b/crates/vm/src/warn.rs index 535bfbd0555..3d8e7cb6fb1 100644 --- a/crates/vm/src/warn.rs +++ b/crates/vm/src/warn.rs @@ -287,7 +287,7 @@ fn already_warned( let version_matches = version_obj.as_ref().is_some_and(|v| { v.try_int(vm) - .is_ok_and(|i| i.payload.as_u32_mask() as usize == current_version) + .is_ok_and(|i| i.as_u32_mask() as usize == current_version) }); if version_matches { @@ -383,7 +383,7 @@ fn filter_search( let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?; let ln: usize = tmp_item.as_slice()[4] .try_int(vm) - .map_or(0, |v| v.payload.as_u32_mask() as _); + .map_or(0, |v| v.as_u32_mask() as _); if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) { return Ok(Some(action.to_owned())); From 38f83b5a2eb0f9fb5b41265869911ae359f697e0 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 27 Sep 2026 00:17:19 +0900 Subject: [PATCH 09/12] Share one conversion between try_to_primitive() and its OverflowError variant Py::try_to_primitive() and try_to_primitive_in_range(), renamed from try_to_primitive_raw(), both call to_primitive(). They differ only in how a negative value for an unsigned type is reported: ValueError for try_to_primitive(), OverflowError for try_to_primitive_in_range(). The target type is bound with `for<'a> TryFrom<&'a BigInt>`, so the methods no longer tie a lifetime to &self. Assisted-by: Claude Code:claude-opus-5-5 --- crates/stdlib/src/array.rs | 2 +- crates/stdlib/src/socket.rs | 6 +++--- crates/vm/src/builtins/int.rs | 28 ++++++++++++++++++++-------- crates/vm/src/stdlib/os.rs | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index ffc4c3cde9d..23bd6c1a79e 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -523,7 +523,7 @@ pub mod array { ($($t:ty,)*) => {$( impl ArrayElement for $t { fn try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - obj.try_index(vm)?.try_to_primitive_raw(vm) + obj.try_index(vm)?.try_to_primitive_in_range(vm) } fn byteswap(self) -> Self { <$t>::swap_bytes(self) diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index d0a44468183..d97bc478038 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2571,7 +2571,7 @@ mod _socket { let addr = Self::from_tuple(tuple, vm)?; let flowinfo = tuple .get(2) - .map(|obj| obj.clone().try_index(vm)?.try_to_primitive_raw(vm)) + .map(|obj| obj.clone().try_index(vm)?.try_to_primitive_in_range(vm)) .transpose()? .unwrap_or(0); let scopeid = tuple @@ -3471,7 +3471,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_LEN")] fn cmsg_len(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.try_to_primitive_in_range(vm)?; host_socket::checked_cmsg_len(length) .ok_or_else(|| vm.new_overflow_error("CMSG_LEN() argument out of range")) } @@ -3479,7 +3479,7 @@ mod _socket { #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "CMSG_SPACE")] fn cmsg_space(length: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let length = length.try_index(vm)?.try_to_primitive_raw(vm)?; + let length = length.try_index(vm)?.try_to_primitive_in_range(vm)?; host_socket::checked_cmsg_space(length) .ok_or_else(|| vm.new_overflow_error("CMSG_SPACE() argument out of range")) } diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index c9041e39325..5835b97848b 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -393,21 +393,33 @@ impl Py { } } - pub fn try_to_primitive<'a, I>(&'a self, vm: &VirtualMachine) -> PyResult + // PyLong_AsUInt32, PyLong_AsUInt64 and the unsigned argument converters: + // a negative value for an unsigned type raises ValueError. + pub fn try_to_primitive(&self, vm: &VirtualMachine) -> PyResult where - I: PrimInt + TryFrom<&'a BigInt>, + I: PrimInt + for<'a> TryFrom<&'a BigInt>, { - if I::min_value() == I::zero() && self.as_bigint().sign() == Sign::Minus { - return Err(vm.new_value_error("can't convert negative number to unsigned")); - } + self.to_primitive(true, vm) + } - self.try_to_primitive_raw(vm) + // PyLong_AsUnsignedLong, PyLong_AsSize_t: a negative value for an unsigned + // type is out of range like any other and raises OverflowError. + pub fn try_to_primitive_in_range(&self, vm: &VirtualMachine) -> PyResult + where + I: PrimInt + for<'a> TryFrom<&'a BigInt>, + { + self.to_primitive(false, vm) } - pub fn try_to_primitive_raw<'a, I>(&'a self, vm: &VirtualMachine) -> PyResult + // PyLong_AsNativeBytes with or without Py_ASNATIVEBYTES_REJECT_NEGATIVE + fn to_primitive(&self, reject_negative: bool, vm: &VirtualMachine) -> PyResult where - I: PrimInt + TryFrom<&'a BigInt>, + I: PrimInt + for<'a> TryFrom<&'a BigInt>, { + if reject_negative && I::min_value() == I::zero() && self.as_bigint().sign() == Sign::Minus + { + return Err(vm.new_value_error("can't convert negative number to unsigned")); + } I::try_from(self.as_bigint()).map_err(|_| { vm.new_overflow_error(format!( "Python int too large to convert to Rust {}", diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index f2b9e861bda..4aa78be349a 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -2123,7 +2123,7 @@ pub(super) mod _os { #[pyfunction] fn waitstatus_to_exitcode(status: WaitStatusArgs, vm: &VirtualMachine) -> PyResult { let status = status.status; - let status = status.try_index(vm)?.try_to_primitive_raw::(vm)?; + let status = status.try_index(vm)?.try_to_primitive_in_range::(vm)?; let exitcode = status >> 8; // ExitProcess() accepts an UINT type: // reject exit code which doesn't fit in an UINT From 0952947267251638feab18eeea392da84a5e8500 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 27 Sep 2026 00:17:48 +0900 Subject: [PATCH 10/12] Move PyInt bitwise operators to Py __xor__, __or__, __and__ and their int_op() helper are defined on Py. PyBool calls them without going through the payload. Assisted-by: Claude Code:claude-opus-5-5 --- crates/vm/src/builtins/bool.rs | 6 ++--- crates/vm/src/builtins/int.rs | 46 +++++++++++++++++----------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/crates/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs index 269dfed6110..a8c2c019582 100644 --- a/crates/vm/src/builtins/bool.rs +++ b/crates/vm/src/builtins/bool.rs @@ -139,7 +139,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs || rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.payload.__or__(rhs).to_pyobject(vm) + lhs.__or__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } @@ -153,7 +153,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs && rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.payload.__and__(rhs).to_pyobject(vm) + lhs.__and__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } @@ -167,7 +167,7 @@ impl PyBool { let rhs = get_value(&rhs); (lhs ^ rhs).to_pyobject(vm) } else if let Some(lhs) = lhs.downcast_ref::() { - lhs.payload.__xor__(rhs).to_pyobject(vm) + lhs.__xor__(rhs).to_pyobject(vm) } else { vm.ctx.not_implemented() } diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 5835b97848b..f8238fa6af3 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -312,17 +312,6 @@ impl PyInt { &self.value } - #[inline] - fn int_op(&self, other: &PyObject, op: F) -> PyArithmeticValue - where - F: Fn(&BigInt, &BigInt) -> BigInt, - { - let r = other - .downcast_ref::() - .map(|other| op(&self.value, other.as_bigint())); - PyArithmeticValue::from_option(r) - } - #[inline] fn general_op(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult where @@ -427,6 +416,29 @@ impl Py { )) }) } + + pub(crate) fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue { + self.int_op(&other, |a, b| a ^ b) + } + + pub(crate) fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue { + self.int_op(&other, |a, b| a | b) + } + + pub(crate) fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue { + self.int_op(&other, |a, b| a & b) + } + + #[inline] + fn int_op(&self, other: &PyObject, op: F) -> PyArithmeticValue + where + F: Fn(&BigInt, &BigInt) -> BigInt, + { + let r = other + .downcast_ref::() + .map(|other| op(self.as_bigint(), other.as_bigint())); + PyArithmeticValue::from_option(r) + } } #[derive(FromArgs)] @@ -441,18 +453,6 @@ struct RoundArgs { with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable) )] impl PyInt { - pub(crate) fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(&other, |a, b| a ^ b) - } - - pub(crate) fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(&other, |a, b| a | b) - } - - pub(crate) fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(&other, |a, b| a & b) - } - fn modpow(&self, other: &PyObject, modulus: &PyObject, vm: &VirtualMachine) -> PyResult { if other.downcast_ref::().is_none() { return Ok(vm.ctx.not_implemented()); From 0d1e70bcc020041a2b63c25bb8c6e86342974a47 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 27 Sep 2026 00:31:37 +0900 Subject: [PATCH 11/12] Move PyBytes operators to Py __add__, __contains__, __getitem__ and __mod__ are defined on Py. The subscript, contains and remainder slots call them without going through the payload. Assisted-by: Claude Code:claude-opus-5-5 --- crates/vm/src/builtins/bytes.rs | 48 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/crates/vm/src/builtins/bytes.rs b/crates/vm/src/builtins/bytes.rs index 6a892e99f0f..c36422e6871 100644 --- a/crates/vm/src/builtins/bytes.rs +++ b/crates/vm/src/builtins/bytes.rs @@ -247,24 +247,11 @@ impl PyBytes { Ok(vm.ctx.new_str(zelf.payload.inner.repr_bytes(vm)?)) } - fn __add__(&self, other: ArgBytesLike) -> Vec { - self.inner.add(&other.borrow_buf()) - } - - fn __contains__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let needle = ByteInnerSub::from_contains_arg(needle, vm)?; - self.inner.contains(needle, vm) - } - #[pystaticmethod] fn maketrans(frm: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult> { PyBytesInner::maketrans(frm, to, vm) } - fn __getitem__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { - self._getitem(needle, vm) - } - #[pymethod] fn isalnum(&self) -> bool { self.inner.isalnum() @@ -527,11 +514,6 @@ impl PyBytes { zelf.repeat(value.into_int_ref().try_to_primitive(vm)?, vm) } - fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let formatted = self.inner.cformat(values, vm)?; - Ok(formatted.into()) - } - #[pymethod] fn __getnewargs__(&self, vm: &VirtualMachine) -> PyTupleRef { let param: Vec = self.elements().map(|x| x.to_pyobject(vm)).collect(); @@ -556,6 +538,24 @@ impl Py { self.payload().as_bytes() } + fn __add__(&self, other: ArgBytesLike) -> Vec { + self.payload.inner.add(&other.borrow_buf()) + } + + fn __contains__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let needle = ByteInnerSub::from_contains_arg(needle, vm)?; + self.payload.inner.contains(needle, vm) + } + + fn __getitem__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { + self.payload._getitem(needle, vm) + } + + fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let formatted = self.payload.inner.cformat(values, vm)?; + Ok(formatted.into()) + } + #[pymethod] fn __reduce_ex__( &self, @@ -656,9 +656,9 @@ impl AsMapping for PyBytes { length: atomic_func!(|mapping, _vm| { Ok(PyBytes::mapping_downcast(mapping).as_bytes().len()) }), - subscript: atomic_func!(|mapping, needle, vm| PyBytes::mapping_downcast(mapping) - .payload - ._getitem(needle, vm)), + subscript: atomic_func!( + |mapping, needle, vm| PyBytes::mapping_downcast(mapping).__getitem__(needle, vm) + ), ..PyMappingMethods::NOT_IMPLEMENTED }); &AS_MAPPING @@ -689,9 +689,7 @@ impl AsSequence for PyBytes { .map(|x| vm.ctx.new_bytes(vec![x]).into()) }), contains: atomic_func!(|seq, other, vm| { - PyBytes::sequence_downcast(seq) - .payload - .__contains__(other.to_owned(), vm) + PyBytes::sequence_downcast(seq).__contains__(other.to_owned(), vm) }), ..PySequenceMethods::NOT_IMPLEMENTED }); @@ -704,7 +702,7 @@ impl AsNumber for PyBytes { static AS_NUMBER: PyNumberMethods = PyNumberMethods { remainder: Some(|a, b, vm| { if let Some(a) = a.downcast_ref::() { - a.payload().__mod__(b.to_owned(), vm).to_pyresult(vm) + a.__mod__(b.to_owned(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } From e92674fa7a3830d7468f018982497ba76296ae7b Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 27 Sep 2026 00:41:43 +0900 Subject: [PATCH 12/12] Move PyInt general_op() and modpow() to Py The power slot calls them on Py without going through the payload. Assisted-by: Claude Code:claude-opus-5-5 --- crates/vm/src/builtins/int.rs | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index f8238fa6af3..dac4e72235f 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -311,18 +311,6 @@ impl PyInt { pub const fn as_bigint(&self) -> &BigInt { &self.value } - - #[inline] - fn general_op(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult - where - F: Fn(&BigInt, &BigInt) -> PyResult, - { - if let Some(other) = other.downcast_ref::() { - op(&self.value, other.as_bigint()) - } else { - Ok(vm.ctx.not_implemented()) - } - } } impl Py { @@ -439,25 +427,24 @@ impl Py { .map(|other| op(self.as_bigint(), other.as_bigint())); PyArithmeticValue::from_option(r) } -} -#[derive(FromArgs)] -struct RoundArgs { - #[pyarg(positional, optional)] - ndigits: Option, -} + #[inline] + fn general_op(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult + where + F: Fn(&BigInt, &BigInt) -> PyResult, + { + if let Some(other) = other.downcast_ref::() { + op(self.as_bigint(), other.as_bigint()) + } else { + Ok(vm.ctx.not_implemented()) + } + } -#[pyclass( - itemsize = 4, - flags(BASETYPE, _MATCH_SELF), - with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable) -)] -impl PyInt { fn modpow(&self, other: &PyObject, modulus: &PyObject, vm: &VirtualMachine) -> PyResult { - if other.downcast_ref::().is_none() { + if other.downcast_ref::().is_none() { return Ok(vm.ctx.not_implemented()); } - let modulus = match modulus.downcast_ref::() { + let modulus = match modulus.downcast_ref::() { Some(val) => val.as_bigint(), None => return Ok(vm.ctx.not_implemented()), }; @@ -496,7 +483,20 @@ impl PyInt { vm, ) } +} + +#[derive(FromArgs)] +struct RoundArgs { + #[pyarg(positional, optional)] + ndigits: Option, +} +#[pyclass( + itemsize = 4, + flags(BASETYPE, _MATCH_SELF), + with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable) +)] +impl PyInt { #[pymethod] fn __round__(zelf: PyRef, args: RoundArgs, vm: &VirtualMachine) -> PyRef { if let Some(ndigits) = args.ndigits { @@ -790,9 +790,9 @@ impl PyInt { power: Some(|a, b, c, vm| { if let Some(a) = a.downcast_ref::() { if vm.is_none(c) { - a.payload.general_op(b, |a, b| inner_pow(a, b, vm), vm) + a.general_op(b, |a, b| inner_pow(a, b, vm), vm) } else { - a.payload.modpow(b, c, vm) + a.modpow(b, c, vm) } } else { Ok(vm.ctx.not_implemented())