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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions crates/capi/src/descrobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::pystate::with_vm;
use crate::util::{CStrExt, FfiPtrExt};
use core::ffi::{c_char, c_int, c_void};
use rustpython_vm::builtins::{
DescriptorMemberDef, MemberAccess, MemberKind, PY_RELATIVE_OFFSET, PyDescriptorOwned, PyGetSet,
PyMappingProxy, PyMemberDescriptor, PyType,
DescriptorMemberDef, MemberAccess, MemberKind, PyDescriptorOwned, PyGetSet, PyMappingProxy,
PyMemberDescriptor, PyMemberFlags, PyType,
};
use rustpython_vm::common::lock::PyRwLock;
use rustpython_vm::function::{ItemDoc, PySetterValue};
Expand Down Expand Up @@ -127,12 +127,15 @@ impl PyMemberDef {
)));
};
let mut offset = self.offset;
let mut flags = self.flags;
if flags & PY_RELATIVE_OFFSET != 0 {
// Unknown bits belong to the extension and are kept.
let mut flags = PyMemberFlags::from_bits_retain(self.flags);
// Extension members never carry the internal atomic-storage bit.
flags.remove(PyMemberFlags::ATOMIC);
if flags.contains(PyMemberFlags::RELATIVE_OFFSET) {
// type creation adds tp_basicsize and clears the flag before GetOne.
// `slots.basicsize` is already the full tp_basicsize.
offset += ty.slots.basicsize as isize;
flags &= !PY_RELATIVE_OFFSET;
flags.remove(PyMemberFlags::RELATIVE_OFFSET);
}

let doc = unsafe { self.doc.try_as_str_opt(vm) }?.map_or(ItemDoc::NONE, |doc| {
Expand Down
52 changes: 38 additions & 14 deletions crates/derive-impl/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2122,17 +2122,32 @@ fn member_layout_tokens(readonly: bool) -> TokenStream {
}

fn member_flag_tokens(readonly: bool, audit_read: bool) -> TokenStream {
let mut flag_parts = Vec::new();
if readonly {
flag_parts.push(quote!(::rustpython_vm::builtins::descriptor::PY_READONLY));
}
if audit_read {
flag_parts.push(quote!(::rustpython_vm::builtins::descriptor::PY_AUDIT_READ));
match (readonly, audit_read) {
(false, false) => {
quote!(::rustpython_vm::builtins::descriptor::PyMemberFlags::empty())
}
(true, false) => {
quote!(::rustpython_vm::builtins::descriptor::PyMemberFlags::READONLY)
}
(false, true) => {
quote!(::rustpython_vm::builtins::descriptor::PyMemberFlags::AUDIT_READ)
}
(true, true) => quote!(
::rustpython_vm::builtins::descriptor::PyMemberFlags::READONLY
.union(::rustpython_vm::builtins::descriptor::PyMemberFlags::AUDIT_READ)
),
}
if flag_parts.is_empty() {
quote!(0)
} else {
quote!(#(#flag_parts)|*)
}

fn member_atomic_flag(field_tokens: &TokenStream) -> TokenStream {
quote! {
if ::rustpython_vm::builtins::descriptor::member_atomic_of(
|payload: &Self| &payload.#field_tokens,
) {
::rustpython_vm::builtins::descriptor::PyMemberFlags::ATOMIC
} else {
::rustpython_vm::builtins::descriptor::PyMemberFlags::empty()
}
}
}

Expand Down Expand Up @@ -2196,7 +2211,7 @@ fn build_member(
"#[pymember(path = ...)] is only valid on a field",
));
}
let (offset, check, kind_tokens) = if let Some((field, index)) = field {
let (offset, check, kind_tokens, atomic_flag) = if let Some((field, index)) = field {
let index_tokens = match &field.ident {
Some(ident) => ident.to_token_stream(),
None => syn::Index::from(index).into_token_stream(),
Expand All @@ -2211,6 +2226,9 @@ fn build_member(
+ ::core::mem::offset_of!(Self, #field_tokens) as isize
};
let layout = member_layout_tokens(readonly);
let atomic_flag = member_atomic_flag(&field_tokens);
// Writable fields must implement `MemberCell`, which only atomic
// storage does. That bound is the check.
let check = quote_spanned! { attr.span() =>
let _ = |payload: *const Self| {
fn assert_member_field<T: #layout>(_: *const T) {}
Expand All @@ -2221,7 +2239,7 @@ fn build_member(
};
};
let kind_tokens = member_kind_tokens(kind.as_deref(), Some(&field_tokens), attr.span())?;
(offset, check, kind_tokens)
(offset, check, kind_tokens, atomic_flag)
} else {
let Some(offset_expr) = offset_expr else {
return Err(syn::Error::new(
Expand All @@ -2230,19 +2248,25 @@ fn build_member(
));
};
let kind_tokens = member_kind_tokens(kind.as_deref(), None, attr.span())?;
(offset_expr, quote!(), kind_tokens)
(
offset_expr,
quote!(),
kind_tokens,
quote!(::rustpython_vm::builtins::descriptor::PyMemberFlags::empty()),
)
};
let doc = if meta.suppress_doc()? {
quote!(::rustpython_vm::function::ItemDoc::NONE)
} else {
attr_doc_expr(Some(class_ty), &name, rust_doc)
};
let base_flags = member_flag_tokens(readonly, audit_read);
Ok(BuiltMember {
name,
cfgs: cfgs.to_vec(),
kind: kind_tokens,
offset,
flags: member_flag_tokens(readonly, audit_read),
flags: quote!(#base_flags.union(#atomic_flag)),
doc,
check,
span: attr.span(),
Expand Down
9 changes: 7 additions & 2 deletions crates/stdlib/src/elementtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1968,8 +1968,8 @@ pub(crate) mod _elementtree {
};

let entity = vm.ctx.new_dict();
let _previous = unsafe { zelf.entity.swap(Some(entity.into())) };
let _previous = unsafe { zelf.target.swap(Some(target.clone())) };
// Lookups can fail. Build the whole state before publishing it, so a
// failed re-init leaves the previous parser, entity, and target in place.
let handlers = XMLParserState {
parser: Some(parser.clone()),
names: Some(vm.ctx.new_dict()),
Expand All @@ -1992,6 +1992,11 @@ pub(crate) mod _elementtree {
let has_pi = handlers.handle_pi.is_some();
let has_ns = handlers.handle_start_ns.is_some() || handlers.handle_end_ns.is_some();
*zelf.state.write() = handlers;
// SAFETY: `swap` returns the previous object. `_previous` holds it
// until `init` returns, so a `deref` of the old pointer stays valid
// for the rest of this call.
let _previous = unsafe { zelf.entity.swap(Some(entity.into())) };
let _previous = unsafe { zelf.target.swap(Some(target)) };

// Expat calls back into these; they dispatch straight to the
// target (and, when it is our own TreeBuilder, straight into
Expand Down
9 changes: 5 additions & 4 deletions crates/stdlib/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,11 +721,12 @@ mod decl {
udata: AtomicU64,
}

// `ident`/`udata` live in `AtomicU64` and `data` in `AtomicI64`.
// Pointer-sized inputs widen into those cells. The cells are not
// `usize`/`c_long`, so those widths need not match.
const _: () = assert!(
core::mem::size_of::<usize>() == core::mem::size_of::<u64>()
&& core::mem::size_of::<isize>() == core::mem::size_of::<i64>()
&& core::mem::size_of::<std::os::raw::c_ulong>() == core::mem::size_of::<u64>()
&& core::mem::size_of::<std::os::raw::c_long>() == core::mem::size_of::<i64>()
core::mem::size_of::<usize>() <= core::mem::size_of::<u64>()
&& core::mem::size_of::<isize>() <= core::mem::size_of::<i64>()
);

#[derive(FromArgs)]
Expand Down
19 changes: 13 additions & 6 deletions crates/vm/src/builtins/builtin_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,23 @@ impl PyNativeFunction {
vm.ctx.none()
}

// meth_reduce in CPython
// meth_reduce: the name when unbound or bound to a module, otherwise
// `(getattr, (self, name))`. `__module__` is not part of the decision.
#[pymethod]
fn __reduce__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult {
let zelf = zelf.0;
if zelf.zelf.is_none() || zelf.module.deref().is_some() {
Ok(vm.ctx.new_str(zelf.value.name).into())
} else {
// PyModule_Check: the type or a subtype, same as meth_reduce.
if let Some(bound) = zelf
.zelf
.as_ref()
.filter(|bound| !bound.class().is_subtype(vm.ctx.types.module_type))
{
let getattr = vm.builtins.get_attr("getattr", vm)?;
let>
Ok(vm.new_tuple((getattr, (target, zelf.value.name))).into())
Ok(vm
.new_tuple((getattr, (bound.clone(), zelf.value.name)))
.into())
} else {
Ok(vm.ctx.new_str(zelf.value.name).into())
}
}

Expand Down
Loading
Loading