Skip to content

Commit 55d3f3a

Browse files
committed
subclass trial 2
1 parent 0af0000 commit 55d3f3a

File tree

7 files changed

+70
-27
lines changed

7 files changed

+70
-27
lines changed

crates/derive-impl/src/pyclass.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,22 +397,34 @@ fn generate_class_def(
397397
quote! { ::rustpython_vm::builtins::PyBaseObject }
398398
};
399399

400-
// Generate PySubclass impl for types with:
401-
// - base class specified
402-
// - #[repr(transparent)] (required for safe transmute)
403-
// - not PyStructSequence
400+
// Generate PySubclass impl for #[repr(transparent)] types with base class
401+
// (tuple struct assumed, so &self.0 works)
404402
let subclass_impl = if !is_pystruct && is_repr_transparent {
405403
base.as_ref().map(|typ| {
406404
quote! {
407405
impl ::rustpython_vm::class::PySubclass for #ident {
408406
type Base = #typ;
407+
408+
#[inline]
409+
fn as_base(&self) -> &Self::Base {
410+
&self.0
411+
}
409412
}
410413
}
411414
})
412415
} else {
413416
None
414417
};
415418

419+
// Generate PySubclassTransparent marker for #[repr(transparent)] types
420+
let transparent_impl = if !is_pystruct && is_repr_transparent && base.is_some() {
421+
Some(quote! {
422+
impl ::rustpython_vm::class::PySubclassTransparent for #ident {}
423+
})
424+
} else {
425+
None
426+
};
427+
416428
let tokens = quote! {
417429
impl ::rustpython_vm::class::PyClassDef for #ident {
418430
const NAME: &'static str = #name;
@@ -439,6 +451,7 @@ fn generate_class_def(
439451
}
440452

441453
#subclass_impl
454+
#transparent_impl
442455
};
443456
Ok(tokens)
444457
}

crates/vm/src/builtins/bool.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ impl PyPayload for PyBool {
107107
) -> PyBaseExceptionRef {
108108
vm.new_downcast_type_error(class, obj)
109109
}
110-
Err(raise_downcast_type_error(vm, Self::class(&vm.ctx), obj))
110+
Err(crate::object::cold_downcast_type_error(
111+
vm,
112+
Self::class(&vm.ctx),
113+
obj,
114+
))
111115
}
112116
}
113117

crates/vm/src/builtins/int.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ impl PyInt {
256256
Ok(vm
257257
.ctx
258258
.new_bool(!value.into().eq(&BigInt::zero()))
259-
.into_base())
259+
.into_base_ref())
260260
} else {
261261
Self::from(value).into_ref_with_type(vm, cls)
262262
}

crates/vm/src/class.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,30 @@ pub trait PyClassImpl: PyClassDef {
173173
}
174174
}
175175

176-
/// Marker trait for Python subclasses with `#[repr(transparent)]` newtype pattern.
176+
/// Trait for Python subclasses that can provide a reference to their base type.
177177
///
178178
/// This trait is automatically implemented by the `#[pyclass]` macro when
179-
/// `base = SomeType` is specified. It enables type-safe conversion between
180-
/// `PyRef<Subclass>` and `PyRef<Base>`.
179+
/// `base = SomeType` is specified. It provides safe reference access to the
180+
/// base type's payload.
181+
///
182+
/// For subclasses with `#[repr(transparent)]`, see also [`PySubclassTransparent`]
183+
/// which enables ownership transfer via `into_base()`.
184+
pub trait PySubclass: crate::PyPayload {
185+
type Base: crate::PyPayload;
186+
187+
/// Returns a reference to the base type's payload.
188+
fn as_base(&self) -> &Self::Base;
189+
}
190+
191+
/// Marker trait for `#[repr(transparent)]` subclasses.
192+
///
193+
/// This trait enables ownership transfer from `PyRef<Self>` to `PyRef<Self::Base>`
194+
/// via the `into_base_ref()` method. Only types with identical memory layout to their
195+
/// base type (i.e., `#[repr(transparent)]` newtypes) should implement this trait.
181196
///
182197
/// # Safety
183198
///
184199
/// Implementors must ensure:
185200
/// - The type uses `#[repr(transparent)]` with the Base type as the only field
186201
/// - Memory layout is identical to the Base type
187-
pub trait PySubclass: crate::PyPayload {
188-
type Base: crate::PyPayload;
189-
}
202+
pub trait PySubclassTransparent: PySubclass {}

crates/vm/src/object/core.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,18 +1081,29 @@ impl<T: PyObjectPayload> PyRef<T> {
10811081
}
10821082

10831083
impl<T: crate::class::PySubclass> PyRef<T> {
1084-
/// Converts this reference to a reference of the base type.
1084+
/// Returns a reference to the base type's payload.
1085+
#[inline]
1086+
pub fn as_base(&self) -> &T::Base {
1087+
(**self).as_base()
1088+
}
1089+
}
1090+
1091+
impl<T: crate::class::PySubclassTransparent> PyRef<T> {
1092+
/// Converts this reference to the base type (ownership transfer).
10851093
///
1086-
/// This is safe because T has `#[repr(transparent)]` layout with T::Base.
1094+
/// Only available for `#[repr(transparent)]` types where memory layout
1095+
/// is identical to the base type.
10871096
#[inline]
1088-
pub fn into_base(self) -> PyRef<T::Base> {
1097+
pub fn into_base_ref(self) -> PyRef<T::Base> {
10891098
// SAFETY: #[repr(transparent)] guarantees same memory layout
10901099
unsafe { std::mem::transmute(self) }
10911100
}
10921101

1093-
/// Returns a reference to the base type.
1102+
/// Returns a reference to this as a PyRef of the base type.
1103+
///
1104+
/// Only available for `#[repr(transparent)]` types.
10941105
#[inline]
1095-
pub fn as_base(&self) -> &PyRef<T::Base> {
1106+
pub fn as_base_ref(&self) -> &PyRef<T::Base> {
10961107
// SAFETY: #[repr(transparent)] guarantees same memory layout
10971108
unsafe { std::mem::transmute(self) }
10981109
}

crates/vm/src/object/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ pub use self::core::*;
88
pub use self::ext::*;
99
pub use self::payload::*;
1010
pub use traverse::{MaybeTraverse, Traverse, TraverseFn};
11+
12+
pub(crate) use self::payload::cold_downcast_type_error;

crates/vm/src/object/payload.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ cfg_if::cfg_if! {
1616
}
1717
}
1818

19+
#[cold]
20+
pub(crate) fn cold_downcast_type_error(
21+
vm: &VirtualMachine,
22+
class: &Py<PyType>,
23+
obj: &PyObject,
24+
) -> PyBaseExceptionRef {
25+
vm.new_downcast_type_error(class, obj)
26+
}
27+
1928
pub trait PyPayload:
2029
std::fmt::Debug + MaybeTraverse + PyThreadingConstraint + Sized + 'static
2130
{
@@ -35,17 +44,8 @@ pub trait PyPayload:
3544
return Ok(());
3645
}
3746

38-
#[cold]
39-
fn raise_downcast_type_error(
40-
vm: &VirtualMachine,
41-
class: &Py<PyType>,
42-
obj: &PyObject,
43-
) -> PyBaseExceptionRef {
44-
vm.new_downcast_type_error(class, obj)
45-
}
46-
4747
let class = Self::class(&vm.ctx);
48-
Err(raise_downcast_type_error(vm, class, obj))
48+
Err(cold_downcast_type_error(vm, class, obj))
4949
}
5050

5151
fn class(ctx: &Context) -> &'static Py<PyType>;

0 commit comments

Comments
 (0)