mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Convert native base references without consulting the Python class (#9090)
PyRef::into_base and Py::to_base cast the pointer directly instead of going through a class-based downcast. into_base no longer reaches unreachable_unchecked when the object's MRO omits the native base, and to_base drops the equivalent debug assertion. Extracted from #8963. Assisted-by: Codex:gpt-6 Assisted-by: Claude:claude-opus-5-5 Co-authored-by: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com>
This commit is contained in:
1 parent
7c3c7a73c7
commit
0803a55df5
1 file changed
+38
-10
@@ -2904,16 +2904,14 @@ where
|
||||
T::Base: core::fmt::Debug,
|
||||
{
|
||||
/// Converts this reference to the base type (ownership transfer).
|
||||
/// # Safety
|
||||
/// T and T::Base must have compatible layouts in size_of::<T::Base>() bytes.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_base(self) -> PyRef<T::Base> {
|
||||
let obj: PyObjectRef = self.into();
|
||||
match obj.downcast() {
|
||||
Ok(base_ref) => base_ref,
|
||||
Err(_) => unsafe { core::hint::unreachable_unchecked() },
|
||||
}
|
||||
// PySubclass establishes the physical prefix; Python may have changed
|
||||
// the object's class or MRO since this typed reference was constructed.
|
||||
let ptr = self.ptr.cast();
|
||||
core::mem::forget(self);
|
||||
PyRef { ptr }
|
||||
}
|
||||
#[inline]
|
||||
#[must_use]
|
||||
@@ -2934,9 +2932,8 @@ impl<T: crate::class::PySubclass> Py<T> {
|
||||
/// Converts `&Py<T>` to `&Py<T::Base>`.
|
||||
#[inline]
|
||||
pub fn to_base(&self) -> &Py<T::Base> {
|
||||
debug_assert!(self.as_object().downcast_ref::<T::Base>().is_some());
|
||||
// SAFETY: T is #[repr(transparent)] over T::Base,
|
||||
// so Py<T> and Py<T::Base> have the same layout.
|
||||
// SAFETY: PySubclass guarantees a valid base prefix with matching
|
||||
// payload offsets and compatible object alignment.
|
||||
unsafe { &*(self as *const Self as *const Py<T::Base>) }
|
||||
}
|
||||
|
||||
@@ -3410,6 +3407,37 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_base_conversions_ignore_python_mro() {
|
||||
crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
|
||||
let class = vm.ctx.new_class(
|
||||
None,
|
||||
"HiddenOSErrorBase",
|
||||
vm.ctx.exceptions.os_error.to_owned(),
|
||||
Default::default(),
|
||||
);
|
||||
let marker: PyObjectRef = vm.ctx.new_int(42).into();
|
||||
let error = vm.new_os_subtype_error(class.clone(), None, marker.clone());
|
||||
// A custom MRO can omit native bases without changing the allocation.
|
||||
*class.mro.write() = vec![class.clone(), vm.ctx.types.object_type.to_owned()];
|
||||
assert!(
|
||||
!error
|
||||
.as_object()
|
||||
.downcastable::<crate::exceptions::types::PyException>()
|
||||
);
|
||||
|
||||
assert!(error.to_base().to_base().get_arg(0).unwrap().is(&marker));
|
||||
assert!(
|
||||
error
|
||||
.into_base()
|
||||
.into_base()
|
||||
.get_arg(0)
|
||||
.unwrap()
|
||||
.is(&marker)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_type_basicsize_includes_payload_padding() {
|
||||
use crate::class::PyClassDef;
|
||||
|
||||
Reference in new issue
Block a user