-
Notifications
You must be signed in to change notification settings - Fork 1.4k
TypeData #6403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeData #6403
Conversation
WalkthroughAdds a type-erased, thread-safe per-heap-type storage (TypeDataSlot) and PyType APIs to init/read/write type data; migrates ctypes metadata (StgInfo) out of payload structs into that per-type slot and updates array/pointer/structure/union creation, sizing, and alignment to consult type_data. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller as Requester
participant Factory as create_array_type_with_stg_info
participant PyType as PyType (heap)
participant HeapExt as HeapTypeExt.type_data
participant Slot as TypeDataSlot (StgInfo)
Caller->>Factory: request new array type (StgInfo)
Factory->>PyType: allocate heap type (new_heap / new_heap_inner)
PyType->>HeapExt: initialize type_data (PyRwLock::new(None))
Factory->>Slot: TypeDataSlot::new(StgInfo)
Factory->>PyType: PyType::init_type_data<StgInfo>(slot)
PyType->>HeapExt: write-lock type_data -> store Slot
PyType-->>Factory: Ok (type created)
Factory-->>Caller: return new array type object
Note over Caller,PyType: Later — size/alignment lookup
Caller->>PyType: get_type_data<StgInfo>()
PyType->>HeapExt: read-lock type_data -> map to StgInfo
HeapExt->>Slot: downcast/get StgInfo
Slot-->>PyType: &StgInfo (TypeDataRef)
PyType-->>Caller: Option<TypeDataRef>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
crates/vm/src/builtins/type.rs
Outdated
|
|
||
| fn deref(&self) -> &Self::Target { | ||
| // SAFETY: try_new already verified the type matches | ||
| self.guard.as_ref().unwrap().get::<T>().unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because try_new already validated it, we can use unwrap_unchecked right?
crates/vm/src/builtins/type.rs
Outdated
|
|
||
| fn deref(&self) -> &Self::Target { | ||
| // SAFETY: try_new already verified the type matches | ||
| self.guard.as_ref().unwrap().get::<T>().unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
crates/vm/src/builtins/type.rs
Outdated
| impl<T: Any + 'static> std::ops::DerefMut for TypeDataRefMut<'_, T> { | ||
| fn deref_mut(&mut self) -> &mut Self::Target { | ||
| // SAFETY: try_new already verified the type matches | ||
| self.guard.as_mut().unwrap().get_mut::<T>().unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I removed Option from here
0ab4805 to
4e47b6a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (6)
crates/vm/src/stdlib/ctypes/union.rs (1)
159-171: Alignment calculation uses field size directly.In lines 169-170, for unions,
max_alignis set tomax_align.max(size)wheresizeis the field size. The comment "For simple types, alignment == size" is correct for primitive C types, but for composite types (nested structures/unions), this may not hold. This could lead to incorrect alignment for complex union layouts.Consider using a proper alignment calculation similar to
PyCStructType::get_field_alignif the field type is not a simple type.crates/vm/src/builtins/type.rs (1)
606-613: Consider using get_type_data for has_type_data.The current implementation manually reads the lock and checks slot presence. This duplicates logic and holds the read lock for the entire chain of checks.
A simpler implementation could be:
pub fn has_type_data<T: Any + 'static>(&self) -> bool { - self.heaptype_ext.as_ref().is_some_and(|ext| { - ext.type_data - .read() - .as_ref() - .is_some_and(|slot| slot.get::<T>().is_some()) - }) + self.get_type_data::<T>().is_some() }This reuses the existing logic and is more maintainable.
crates/vm/src/stdlib/ctypes/base.rs (2)
237-249: Consider adding debug logging for failed_type_resolution.When
get_stg_infofalls back toStgInfo::default(), the caller has no indication that the type resolution failed. This could make debugging issues harder. Consider adding atracingor debug-level log when the fallback path is taken.
766-773: Alignment parameter uses element_size, which may not always be correct.The
StgInfo::new_array()call passeselement_sizeas the alignment. While this is often correct (e.g.,c_intis 4 bytes with 4-byte alignment), some types may have different alignment requirements. Consider deriving alignment from the actual type's alignment rather than assuming it equals element size.+ // Get alignment from type if available, otherwise use element_size + let align = if let Ok(type_attr) = cls.as_object().get_attr("_type_", vm) { + if let Ok(s) = type_attr.str(vm) { + let s = s.to_string(); + if s.len() == 1 { + super::_ctypes::get_align(&s) + } else { + element_size + } + } else { + element_size + } + } else { + element_size + }; let stg_info = super::util::StgInfo::new_array( total_size, - element_size, + align, n as usize, cls.clone().into(), element_size, );crates/vm/src/types/slot.rs (1)
26-58: Thetype_idfield is redundant withdowncast_refbut provides a fast-path check.The
get<T>()method first checkstype_id == TypeId::of::<T>()before callingdowncast_ref(). Sincedowncast_ref()internally performs the same type ID check, this is slightly redundant. However, the explicit check may serve as documentation or a fast-path. If intentional, consider adding a comment explaining this is a defensive check./// Get a reference to the data if the type matches. pub fn get<T: Any + 'static>(&self) -> Option<&T> { + // Fast-path type check before downcast (downcast_ref also checks internally) if self.type_id == TypeId::of::<T>() { self.data.downcast_ref() } else { None } }crates/vm/src/stdlib/ctypes/array.rs (1)
668-758: Consider extracting commonin_dlllogic to reduce duplication.Both
PyCArrayType::in_dll(lines 126-211) andPyCArray::in_dll(lines 668-758) contain nearly identical code for:
- Getting the library handle
- Looking up the library in cache
- Getting the symbol address
This could be extracted into a helper function to reduce maintenance burden.
// Helper function to extract common logic fn get_symbol_address( dll: &PyObjectRef, name: &str, vm: &VirtualMachine, ) -> PyResult<usize> { use libloading::Symbol; let handle = if let Ok(int_handle) = dll.try_int(vm) { int_handle .as_bigint() .to_usize() .ok_or_else(|| vm.new_value_error("Invalid library handle".to_owned()))? } else { dll.get_attr("_handle", vm)? .try_int(vm)? .as_bigint() .to_usize() .ok_or_else(|| vm.new_value_error("Invalid library handle".to_owned()))? }; let library_cache = crate::stdlib::ctypes::library::libcache().read(); let library = library_cache .get_lib(handle) .ok_or_else(|| vm.new_attribute_error("Library not found".to_owned()))?; let symbol_name = format!("{}\0", name); let inner_lib = library.lib.lock(); if let Some(lib) = &*inner_lib { unsafe { let symbol: Symbol<'_, *mut u8> = lib.get(symbol_name.as_bytes()).map_err(|e| { vm.new_attribute_error(format!("{}: symbol '{}' not found", e, name)) })?; Ok(*symbol as usize) } } else { Err(vm.new_attribute_error("Library is closed".to_owned())) } }
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
crates/vm/src/builtins/type.rs(5 hunks)crates/vm/src/stdlib/ctypes.rs(2 hunks)crates/vm/src/stdlib/ctypes/array.rs(5 hunks)crates/vm/src/stdlib/ctypes/base.rs(3 hunks)crates/vm/src/stdlib/ctypes/pointer.rs(1 hunks)crates/vm/src/stdlib/ctypes/structure.rs(3 hunks)crates/vm/src/stdlib/ctypes/union.rs(1 hunks)crates/vm/src/stdlib/ctypes/util.rs(3 hunks)crates/vm/src/types/slot.rs(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Follow the default rustfmt code style by runningcargo fmtto format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
crates/vm/src/stdlib/ctypes/pointer.rscrates/vm/src/stdlib/ctypes/union.rscrates/vm/src/stdlib/ctypes/base.rscrates/vm/src/stdlib/ctypes.rscrates/vm/src/stdlib/ctypes/structure.rscrates/vm/src/types/slot.rscrates/vm/src/stdlib/ctypes/util.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/ctypes/array.rs
🧠 Learnings (1)
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
Applied to files:
crates/vm/src/stdlib/ctypes/pointer.rs
🧬 Code graph analysis (6)
crates/vm/src/stdlib/ctypes/pointer.rs (2)
crates/vm/src/stdlib/ctypes/array.rs (2)
__mul__(95-123)create_array_type_with_stginfo(30-66)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/stdlib/ctypes/base.rs (2)
crates/vm/src/stdlib/ctypes/array.rs (1)
create_array_type_with_stginfo(30-66)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/stdlib/ctypes/structure.rs (2)
crates/vm/src/stdlib/ctypes/array.rs (1)
create_array_type_with_stginfo(30-66)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/types/slot.rs (2)
crates/vm/src/object/payload.rs (1)
std(22-22)crates/vm/src/builtins/type.rs (1)
slot(611-611)
crates/vm/src/stdlib/ctypes/util.rs (3)
crates/vm/src/stdlib/ctypes.rs (3)
std(418-418)std(444-444)std(696-696)crates/vm/src/stdlib/ctypes/array.rs (5)
std(278-278)std(281-281)std(284-284)fmt(248-253)length(87-92)crates/vm/src/stdlib/ctypes/field.rs (1)
size(259-261)
crates/vm/src/builtins/type.rs (1)
crates/vm/src/types/slot.rs (6)
new(34-39)new(191-197)try_new(68-72)try_new(91-97)slot(69-69)slot(93-93)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Ensure compilation on various targets
- GitHub Check: Run rust tests (macos-latest)
- GitHub Check: Run snippets and cpython tests on wasm-wasi
- GitHub Check: Run rust tests (windows-latest)
- GitHub Check: Run snippets and cpython tests (ubuntu-latest)
- GitHub Check: Run rust tests (ubuntu-latest)
- GitHub Check: Run tests under miri
- GitHub Check: Run snippets and cpython tests (macos-latest)
- GitHub Check: Run snippets and cpython tests (windows-latest)
- GitHub Check: Check Rust code with clippy
🔇 Additional comments (17)
crates/vm/src/stdlib/ctypes/structure.rs (2)
18-19: LGTM - Clean simplification of PyCStructType.The empty struct with
#[derive(Debug, PyPayload, Default)]is appropriate for a metaclass that stores its data via the newTypeDataSlotmechanism rather than inline fields.
173-180: The concern about alignment is unfounded. Examining allStgInfo::new_arraycalls across the ctypes module shows that passingelement_sizeas the alignment parameter is the consistent and intentional pattern used in:
structure.rs(lines 173-180)pointer.rs(lines 26-31)array.rs(lines 114-119)base.rs(lines 766-771)In ctypes, the alignment of an array is correctly defined as the alignment of its element type. The code is correct.
Likely an incorrect or invalid review comment.
crates/vm/src/stdlib/ctypes/union.rs (1)
16-17: LGTM - Consistent with PyCStructType refactoring.The empty struct with derives matches the pattern used in
structure.rs, maintaining consistency across ctypes metaclasses.crates/vm/src/stdlib/ctypes/pointer.rs (2)
12-13: LGTM - Consistent metaclass pattern.The empty struct with
#[derive(PyPayload, Debug, Default)]follows the same pattern as other ctypes metaclasses.
26-33: Pointer array creation correctly uses pointer size for alignment.For pointer arrays, using
element_size(which equalsstd::mem::size_of::<usize>()) as the alignment is correct since pointers are naturally aligned to their own size on all common platforms.crates/vm/src/stdlib/ctypes.rs (2)
398-408: LGTM - TypeDataSlot-based size lookup with proper fallbacks.The implementation correctly prioritizes TypeDataSlot on the instance's class first, then checks if the object itself is a type with TypeDataSlot, before falling back to legacy paths. This ensures backward compatibility while leveraging the new type-data infrastructure.
670-680: LGTM - Alignment lookup mirrors size_of pattern.The alignment function follows the same lookup strategy as
size_of, ensuring consistent behavior across both operations.crates/vm/src/builtins/type.rs (4)
70-70: LGTM - New type_data field with appropriate locking.Adding
pub type_data: PyRwLock<Option<TypeDataSlot>>toHeapTypeExtprovides thread-safe storage for per-type data. TheOptionwrapper allows for lazy initialization.
575-587: LGTM - init_type_data with proper guards.The implementation correctly:
- Returns an error for non-heap types
- Prevents double-initialization by checking if data already exists
- Uses proper write locking for the update
591-603: LGTM - Clean accessor pattern with guard types.Both
get_type_dataandget_type_data_mutproperly delegate toTypeDataRef::try_newandTypeDataRefMut::try_newrespectively, which handle the type checking and guard mapping internally.
209-209: LGTM - Consistent initialization across heap type construction paths.Both
new_heap(line 209) andslot_new(line 1218) properly initializetype_datatoPyRwLock::new(None), ensuring all heap types start with uninitialized type data.Also applies to: 1218-1218
crates/vm/src/stdlib/ctypes/util.rs (2)
25-38: LGTM: ManualDebugimplementation is correct.The manual
Debugimpl properly formats all fields ofStgInfo. This was necessary because the derive was removed (likely becausePyObjectRefdoesn't implementDebugin a useful way or at all).
55-87: LGTM: Constructors are well-structured.The
new()andnew_array()factory methods provide clear, type-safe construction with appropriate defaults. Thenew_array()constructor properly captures all array-specific metadata.crates/vm/src/stdlib/ctypes/base.rs (1)
232-232: LGTM: Empty struct aligns with new type-data architecture.The migration from embedded
stg_infofield to an empty struct is consistent with the PR's goal of storing metadata viaTypeDataSloton the type object itself rather than per-instance.crates/vm/src/types/slot.rs (1)
60-112: LGTM: Guard wrappers provide safe, zero-cost type-erased access.The
TypeDataRefandTypeDataRefMutwrappers correctly usetry_mapto safely downcast while holding the lock, and theDeref/DerefMutimplementations enable ergonomic access to the underlying data.crates/vm/src/stdlib/ctypes/array.rs (2)
29-65: LGTM: Factory function properly creates array types with type-data storage.The
create_array_type_with_stginfofunction correctly:
- Creates a new type with
PyCArrayTypeas metaclass andPyCArrayas base- Attempts to initialize type-data with
init_type_data- Falls back to updating existing type-data if already initialized
The fallback update pattern (lines 58-62) handles the case where type data was already set, which is a reasonable defensive approach.
78-92: LGTM: Accessors safely handle missing type-data.The
typandlengthgetters use chainedOptionoperations with appropriate fallbacks (vm.ctx.none()and0), which is consistent with Python's duck-typing semantics where attributes may be missing.
| // Get size from the array type via TypeDataSlot | ||
| let type_ref = zelf.downcast_ref::<PyType>().unwrap(); | ||
| let (element_type, length, element_size) = type_ref | ||
| .get_type_data::<StgInfo>() | ||
| .map(|stg| { | ||
| ( | ||
| stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()), | ||
| stg.length, | ||
| stg.element_size, | ||
| ) | ||
| }) | ||
| .unwrap_or_else(|| (vm.ctx.none(), 0, 0)); | ||
| let total_size = element_size * length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same unwrap issue in in_dll.
Line 173 has the same .unwrap() pattern on downcast_ref::<PyType>(). Apply the same fix as suggested for __mul__.
// Get size from the array type via TypeDataSlot
- let type_ref = zelf.downcast_ref::<PyType>().unwrap();
+ let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| {
+ vm.new_type_error("in_dll requires a type object".to_owned())
+ })?;
let (element_type, length, element_size) = type_ref📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Get size from the array type via TypeDataSlot | |
| let type_ref = zelf.downcast_ref::<PyType>().unwrap(); | |
| let (element_type, length, element_size) = type_ref | |
| .get_type_data::<StgInfo>() | |
| .map(|stg| { | |
| ( | |
| stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()), | |
| stg.length, | |
| stg.element_size, | |
| ) | |
| }) | |
| .unwrap_or_else(|| (vm.ctx.none(), 0, 0)); | |
| let total_size = element_size * length; | |
| // Get size from the array type via TypeDataSlot | |
| let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| { | |
| vm.new_type_error("in_dll requires a type object".to_owned()) | |
| })?; | |
| let (element_type, length, element_size) = type_ref | |
| .get_type_data::<StgInfo>() | |
| .map(|stg| { | |
| ( | |
| stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()), | |
| stg.length, | |
| stg.element_size, | |
| ) | |
| }) | |
| .unwrap_or_else(|| (vm.ctx.none(), 0, 0)); | |
| let total_size = element_size * length; |
🤖 Prompt for AI Agents
crates/vm/src/stdlib/ctypes/array.rs around lines 172-184: the code currently
calls zelf.downcast_ref::<PyType>().unwrap(), which can panic; replace the
unwrap with the same defensive pattern used in __mul__: check
downcast_ref::<PyType>() and if it returns None return a Python TypeError (or
appropriate Err) from the function with a clear message instead of panicking,
then proceed to read the TypeDataSlot when you have a valid type_ref.
| // StgInfo is stored in type_data which requires Send + Sync. | ||
| // The PyObjectRef in proto/element_type fields is protected by the type system's locking mechanism. | ||
| // CPython: ctypes objects are not thread-safe by design; users must synchronize access. | ||
| unsafe impl Send for StgInfo {} | ||
| unsafe impl Sync for StgInfo {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for PyObjectRef definition and its trait implementations
rg -n "struct PyObjectRef|type PyObjectRef" --type rust -A 5
# Check if PyObjectRef implements Send and Sync
rg -n "impl.*Send.*for.*PyObject" --type rust -B 2 -A 2
rg -n "impl.*Sync.*for.*PyObject" --type rust -B 2 -A 2
# Find other unsafe Send/Sync impls containing PyObjectRef or similar patterns
rg -n "unsafe impl.*(Send|Sync)" --type rust -B 3 -A 1 | head -100Repository: RustPython/RustPython
Length of output: 7774
Add cfg_if feature gate to match PyObjectRef's thread-safety bounds.
The unsafe impl Send and unsafe impl Sync for StgInfo must be conditionally gated with the threading feature, consistent with PyObjectRef's own implementations (crates/vm/src/object/core.rs:478-481). Currently, StgInfo claims to be thread-safe unconditionally, but contains PyObjectRef fields that only implement Send/Sync when threading is enabled. Additionally, clarify the "type system's locking mechanism" comment—it should explain the actual synchronization strategy (likely the GIL) rather than remaining vague.
🤖 Prompt for AI Agents
In crates/vm/src/stdlib/ctypes/util.rs around lines 19-23, the unsafe impls for
Send and Sync on StgInfo are unconditional but StgInfo contains PyObjectRef
fields which only implement Send/Sync when the "threading" feature is enabled;
change the implementation to gate the unsafe impl Send and unsafe impl Sync with
the same cfg (e.g. cfg_if or #[cfg(feature = "threading")]) used for PyObjectRef
in object/core.rs so the bounds match, and update the preceding comment to
explicitly state that synchronization is provided by the GIL (or the specific
type-system locking mechanism) rather than the vague phrase "type system's
locking mechanism."
4e47b6a to
1707f96
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/ctypes/union.rs (1)
167-171: Alignment calculation oversimplified for non-simple types.The comment states "alignment == size" applies to simple types, but line 170 applies this logic unconditionally to all field types. For struct/union/array fields, this may produce incorrect alignment values.
Consider using
PyCUnionType::get_field_sizeplus a proper alignment calculation or exposeget_field_alignfrom the union type:let size = PyCUnionType::get_field_size(&field_type, vm)?; max_size = max_size.max(size); - // For simple types, alignment == size - max_align = max_align.max(size); + // Get proper alignment for the field type + let field_align = PyCStructType::get_field_align(&field_type, vm); + max_align = max_align.max(field_align);
♻️ Duplicate comments (2)
crates/vm/src/stdlib/ctypes/util.rs (1)
19-23: Address previous review: Addcfgfeature gate for thread-safety bounds.As flagged in the previous review, the
unsafe impl Sendandunsafe impl Syncshould be conditionally gated with thethreadingfeature to matchPyObjectRef's own implementations. The comment should also clarify the actual synchronization mechanism (e.g., GIL).-// StgInfo is stored in type_data which requires Send + Sync. -// The PyObjectRef in proto/element_type fields is protected by the type system's locking mechanism. -// CPython: ctypes objects are not thread-safe by design; users must synchronize access. -unsafe impl Send for StgInfo {} -unsafe impl Sync for StgInfo {} +// StgInfo is stored in type_data which requires Send + Sync. +// The PyObjectRef fields are protected by Python's GIL when threading is enabled. +// CPython: ctypes objects are not thread-safe by design; users must synchronize access. +#[cfg(feature = "threading")] +unsafe impl Send for StgInfo {} +#[cfg(feature = "threading")] +unsafe impl Sync for StgInfo {}crates/vm/src/stdlib/ctypes/array.rs (1)
95-123: Avoidunwrap()ondowncast_refand use StgInfo alignment instead of size in__mul__/ metain_dllBoth
__mul__and the metaclass-levelin_dllcurrently do:
let type_ref = zelf.downcast_ref::<PyType>().unwrap();If
zelfis ever not aPyType(e.g. misused from Python or via an internal call), this will panic instead of raising a Python exception. Past review already pointed this out for both call sites; the issue is still present.Additionally,
__mul__passes the inner size as thealignargument toStgInfo::new_array:let (_inner_length, inner_size) = ... stg.size ... ... let stg_info = StgInfo::new_array( total_size, new_element_size, // <-- using size as align n as usize, current_array_type, new_element_size, );Conceptually, array alignment should come from the inner type’s alignment (
stg.align), not from its total size. Usingsizeasalignwill give incorrect alignment for nested arrays and any place that relies onalign(e.g. libffi layouts).A concrete fix that addresses both concerns and makes missing
StgInfoa Python error could look like:@@ #[pymethod] fn __mul__(zelf: PyObjectRef, n: isize, vm: &VirtualMachine) -> PyResult { if n < 0 { return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}"))); } - // Get inner array info from TypeDataSlot - let type_ref = zelf.downcast_ref::<PyType>().unwrap(); - let (_inner_length, inner_size) = type_ref - .get_type_data::<StgInfo>() - .map(|stg| (stg.length, stg.size)) - .unwrap_or((0, 0)); + // Get inner array info from type_data; require `zelf` to be a type with StgInfo + let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| { + vm.new_type_error("descriptor '__mul__' requires a type object".to_owned()) + })?; + let (inner_size, inner_align) = type_ref + .get_type_data::<StgInfo>() + .map(|stg| (stg.size, stg.align)) + .ok_or_else(|| { + vm.new_type_error("array type has no storage info (StgInfo)".to_owned()) + })?; // The element type of the new array is the current array type itself let current_array_type: PyObjectRef = zelf.clone(); - // Element size is the total size of the inner array - let new_element_size = inner_size; - let total_size = new_element_size * (n as usize); - - let stg_info = StgInfo::new_array( - total_size, - new_element_size, - n as usize, - current_array_type, - new_element_size, - ); + // Each element is the full inner array + let element_size = inner_size; + let total_size = element_size * (n as usize); + + let stg_info = StgInfo::new_array( + total_size, + inner_align, + n as usize, + current_array_type, + element_size, + ); create_array_type_with_stginfo(stg_info, vm) @@ - // Get size from the array type via TypeDataSlot - let type_ref = zelf.downcast_ref::<PyType>().unwrap(); - let (element_type, length, element_size) = type_ref - .get_type_data::<StgInfo>() - .map(|stg| { - ( - stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()), - stg.length, - stg.element_size, - ) - }) - .unwrap_or_else(|| (vm.ctx.none(), 0, 0)); + // Get size from the array type via type_data + let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| { + vm.new_type_error("in_dll requires a type object".to_owned()) + })?; + let stg = type_ref + .get_type_data::<StgInfo>() + .ok_or_else(|| { + vm.new_type_error("array type has no storage info (StgInfo)".to_owned()) + })?; + let element_type = stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()); + let length = stg.length; + let element_size = stg.element_size;This keeps the normal path unchanged for correctly constructed array types (which always have a
StgInfoset viacreate_array_type_with_stginfo), while:
- eliminating potential panics from
unwrap(), and- using the stored alignment as intended by
StgInfo::new_array.Also applies to: 172-184
🧹 Nitpick comments (3)
crates/vm/src/stdlib/ctypes.rs (1)
410-421: Fix duplicate comment numbering.Lines 410 and 421 both use "// 3." numbering. Consider renumbering for clarity:
- // 3. Instances with cdata buffer + // 3. Instances with cdata buffer if let Some(structure) = obj.downcast_ref::<PyCStructure>() { ... - // 3. Type objects + // 4. Type objects if let Ok(type_ref) = obj.clone().downcast::<crate::builtins::PyType>() {crates/vm/src/builtins/type.rs (1)
571-612: TypeData API onPyTypeis well-scoped; minor simplification possibleThe
init_type_data/get_type_data/get_type_data_mut/has_type_dataAPI cleanly enforces:
- heap-only storage (via
heaptype_extcheck),- single initialization under a write lock, and
- typed access via
TypeDataRef/TypeDataRefMut.One small possible simplification:
has_type_datacould be implemented in terms ofget_type_data::<T>().is_some()to avoid duplicating theTypeId/type-matching logic already encapsulated inTypeDataRef::try_new, but that’s purely cosmetic.crates/vm/src/stdlib/ctypes/array.rs (1)
23-66: Array-type helper and meta layout look good; consider tightening error handling and namingThe refactor to make
PyCArrayTypeempty and push layout into per-typeStgInfostored intype_data, plus:
create_array_type_with_stginfoconstructing a heap type viaPyType::slot_newwithPyCArrayTypeas metaclass, and_type_/_length_onPyCArrayTypereading directly fromStgInfo,is a clean way to centralize ctypes array metadata and matches the new TypeDataSlot design.
Two non-blocking refinements you might consider:
In
create_array_type_with_stginfo, treating anyErrfrominit_type_dataas “already initialized, update in place” can hide real misuse; since this helper always creates a fresh heap type, it would be clearer to either:
- propagate the error as a Python exception, or
debug_assert!thatinit_type_datasucceeds and only fall back to mutation in explicitly reused-type scenarios.
type_name = format!("Array_{}", stg_info.length)can collide for different element types with the same length. If the name is used only for debugging this is fine; if you rely on it for identity or error messages, encoding the element type (e.g. its name) into the string would make types more self-describing.The AsNumber
multiplywiring that callsPyCArrayType::__mul__(a.to_owned(), n, vm)also looks consistent with the metaclass-based protocol.Also applies to: 76-92, 214-225
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
crates/vm/src/builtins/type.rs(5 hunks)crates/vm/src/stdlib/ctypes.rs(2 hunks)crates/vm/src/stdlib/ctypes/array.rs(5 hunks)crates/vm/src/stdlib/ctypes/base.rs(3 hunks)crates/vm/src/stdlib/ctypes/pointer.rs(1 hunks)crates/vm/src/stdlib/ctypes/structure.rs(3 hunks)crates/vm/src/stdlib/ctypes/union.rs(1 hunks)crates/vm/src/stdlib/ctypes/util.rs(3 hunks)crates/vm/src/types/slot.rs(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- crates/vm/src/stdlib/ctypes/base.rs
- crates/vm/src/types/slot.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Follow the default rustfmt code style by runningcargo fmtto format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
crates/vm/src/stdlib/ctypes.rscrates/vm/src/stdlib/ctypes/union.rscrates/vm/src/stdlib/ctypes/structure.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/ctypes/util.rscrates/vm/src/stdlib/ctypes/pointer.rscrates/vm/src/stdlib/ctypes/array.rs
🧠 Learnings (3)
📚 Learning: 2025-06-27T14:47:28.810Z
Learnt from: moreal
Repo: RustPython/RustPython PR: 5847
File: vm/src/stdlib/stat.rs:547-567
Timestamp: 2025-06-27T14:47:28.810Z
Learning: In RustPython's stat module implementation, platform-specific constants like SF_SUPPORTED and SF_SYNTHETIC should be conditionally declared only for the platforms where they're available (e.g., macOS), following CPython's approach of optional declaration using #ifdef checks rather than providing fallback values for other platforms.
Applied to files:
crates/vm/src/stdlib/ctypes/util.rs
📚 Learning: 2025-06-27T14:47:28.810Z
Learnt from: moreal
Repo: RustPython/RustPython PR: 5847
File: vm/src/stdlib/stat.rs:547-567
Timestamp: 2025-06-27T14:47:28.810Z
Learning: In RustPython's stat module implementation, platform-specific constants like SF_SUPPORTED and SF_SYNTHETIC should be conditionally declared only for the platforms where they're available (e.g., macOS), following CPython's approach of optional declaration rather than providing fallback values for other platforms.
Applied to files:
crates/vm/src/stdlib/ctypes/util.rs
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
Applied to files:
crates/vm/src/stdlib/ctypes/pointer.rs
🧬 Code graph analysis (4)
crates/vm/src/builtins/type.rs (1)
crates/vm/src/types/slot.rs (6)
new(33-38)new(190-196)try_new(67-71)try_new(90-96)slot(68-68)slot(92-92)
crates/vm/src/stdlib/ctypes/util.rs (1)
crates/vm/src/stdlib/ctypes.rs (3)
std(418-418)std(444-444)std(696-696)
crates/vm/src/stdlib/ctypes/pointer.rs (3)
crates/vm/src/stdlib/ctypes/array.rs (1)
create_array_type_with_stginfo(30-66)crates/vm/src/stdlib/ctypes.rs (17)
mem(144-144)mem(145-145)mem(146-146)mem(147-147)mem(148-148)mem(149-149)mem(150-150)mem(151-151)mem(152-152)mem(153-153)mem(154-154)mem(155-155)mem(156-156)mem(157-157)mem(158-158)mem(212-212)size_of(391-449)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/stdlib/ctypes/array.rs (1)
crates/vm/src/stdlib/ctypes/util.rs (3)
new(56-67)default(41-52)new_array(70-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Run snippets and cpython tests (windows-latest)
- GitHub Check: Run rust tests (windows-latest)
- GitHub Check: Run rust tests (macos-latest)
- GitHub Check: auto_format
- GitHub Check: Check the WASM package and demo
🔇 Additional comments (10)
crates/vm/src/stdlib/ctypes/pointer.rs (2)
12-13: LGTM: Empty struct aligns with TypeDataSlot refactor.The removal of the
stg_infofield fromPyCPointerTypecorrectly shifts metadata storage to the newTypeDataSlotmechanism.
26-33: Verify:alignset toelement_sizefor pointer arrays.Using
element_size(pointer size) for both alignment and element size is correct for pointer types since pointer alignment equals pointer size. The refactor to usecreate_array_type_with_stginfoproperly centralizes array type creation.crates/vm/src/stdlib/ctypes/union.rs (1)
16-17: LGTM: Empty struct consistent with TypeDataSlot refactor.The removal of internal state from
PyCUnionTypealigns with the PR's pattern of moving metadata toTypeDataSlot.crates/vm/src/stdlib/ctypes.rs (2)
398-408: LGTM: TypeDataSlot-first approach for size resolution.The new logic correctly prioritizes
TypeDataSlotlookup for both instances (viaobj.class()) and type objects (via direct downcast), with proper fallbacks to existing mechanisms.
670-680: LGTM: Consistent TypeDataSlot pattern for alignment.The alignment function mirrors the size_of logic correctly, checking
TypeDataSlotfirst before falling back to existing mechanisms.crates/vm/src/stdlib/ctypes/structure.rs (2)
18-19: LGTM: Empty struct consistent with TypeDataSlot refactor.The pattern matches
PyCPointerTypeandPyCUnionType, properly shifting metadata to the new storage mechanism.
173-180: The current implementation correctly useselement_sizefor alignment, which is the consistent pattern throughout the ctypes module. Both pointer arrays (pointer.rs) and generic arrays (array.rs, base.rs) use the same approach. Additionally,Self::calculate_struct_alignmentdoes not exist and cannot be feasibly implemented—the structure's alignment information is not accessible in this context. The design pattern of using element size as alignment appears intentional and aligns with how C language ctypes spacing works.Likely an incorrect or invalid review comment.
crates/vm/src/stdlib/ctypes/util.rs (2)
69-87: LGTM: Clean API for array-type StgInfo construction.Taking
element_type: PyObjectRefdirectly (rather thanOption<PyObjectRef>) is appropriate since arrays always have an element type. The internal wrapping toSome(element_type)is handled correctly.
40-53: LGTM: Default implementation correctly initializes new fields.The default values (
element_type: None,element_size: 0) are appropriate for non-array types.crates/vm/src/builtins/type.rs (1)
63-70: HeapTypeExttype_datawiring and initialization look correct
HeapTypeExtnow carriestype_data: PyRwLock<Option<TypeDataSlot>>, and both heap-type construction paths (new_heapand the heap path inslot_new) initialize it toPyRwLock::new(None). That ensures:
- static types never have a
type_dataslot (heaptype_ext: Nonethere), and- heap types start with an empty slot, so
init_type_data’s one-shot semantics are reliable.No issues from a correctness or threading perspective.
Also applies to: 201-209, 1205-1219
1707f96 to
7a2b967
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/ctypes/array.rs (1)
126-212: Critical: Same.unwrap()issue inin_dll.Line 173 has the same unsafe
.unwrap()pattern afterdowncast_ref::<PyType>(). This can panic ifzelfis not a type object.Apply the same fix:
// Get size from the array type via TypeDataSlot - let type_ref = zelf.downcast_ref::<PyType>().unwrap(); + let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| { + vm.new_type_error("in_dll requires a type object".to_owned()) + })?; let (element_type, length, element_size) = type_ref
♻️ Duplicate comments (2)
crates/vm/src/stdlib/ctypes/util.rs (1)
19-23: UnsafeSend/Syncimpls need feature gating to matchPyObjectRef.This is a known issue from a previous review. The
unsafe impl Sendandunsafe impl SyncforStgInfoare unconditional, butStgInfocontainsPyObjectReffields (proto,element_type) which only implementSend/Syncwhen thethreadingfeature is enabled. These impls should be conditionally gated with#[cfg(feature = "threading")]to matchPyObjectRef's bounds inobject/core.rs. Additionally, clarify the comment to explicitly state the synchronization mechanism (e.g., the GIL).-// StgInfo is stored in type_data which requires Send + Sync. -// The PyObjectRef in proto/element_type fields is protected by the type system's locking mechanism. -// CPython: ctypes objects are not thread-safe by design; users must synchronize access. -unsafe impl Send for StgInfo {} -unsafe impl Sync for StgInfo {} +// StgInfo is stored in type_data which requires Send + Sync. +// The PyObjectRef in proto/element_type fields is protected by the GIL. +// CPython: ctypes objects are not thread-safe by design; users must synchronize access. +#[cfg(feature = "threading")] +unsafe impl Send for StgInfo {} +#[cfg(feature = "threading")] +unsafe impl Sync for StgInfo {}crates/vm/src/stdlib/ctypes/array.rs (1)
95-123: Critical:.unwrap()can panic ifzelfis not aPyType.Line 101 calls
.unwrap()afterdowncast_ref::<PyType>()without validation. SincezelfisPyObjectRef, this is not statically guaranteed and will panic ifzelfis not a type object.Note: This issue was flagged in previous reviews and marked as addressed, but the
.unwrap()is still present in the current code.Apply this fix to handle the downcast safely:
fn __mul__(zelf: PyObjectRef, n: isize, vm: &VirtualMachine) -> PyResult { if n < 0 { return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}"))); } // Get inner array info from TypeDataSlot - let type_ref = zelf.downcast_ref::<PyType>().unwrap(); + let type_ref = zelf.downcast_ref::<PyType>().ok_or_else(|| { + vm.new_type_error("descriptor '__mul__' requires a type object".to_owned()) + })?; let (_inner_length, inner_size) = type_ref .get_type_data::<StgInfo>() .map(|stg| (stg.length, stg.size)) .unwrap_or((0, 0));
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
crates/vm/src/stdlib/ctypes.rs(2 hunks)crates/vm/src/stdlib/ctypes/array.rs(5 hunks)crates/vm/src/stdlib/ctypes/base.rs(3 hunks)crates/vm/src/stdlib/ctypes/pointer.rs(1 hunks)crates/vm/src/stdlib/ctypes/structure.rs(3 hunks)crates/vm/src/stdlib/ctypes/union.rs(1 hunks)crates/vm/src/stdlib/ctypes/util.rs(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Follow the default rustfmt code style by runningcargo fmtto format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
crates/vm/src/stdlib/ctypes/base.rscrates/vm/src/stdlib/ctypes.rscrates/vm/src/stdlib/ctypes/util.rscrates/vm/src/stdlib/ctypes/union.rscrates/vm/src/stdlib/ctypes/structure.rscrates/vm/src/stdlib/ctypes/array.rscrates/vm/src/stdlib/ctypes/pointer.rs
🧠 Learnings (3)
📚 Learning: 2025-06-27T14:47:28.810Z
Learnt from: moreal
Repo: RustPython/RustPython PR: 5847
File: vm/src/stdlib/stat.rs:547-567
Timestamp: 2025-06-27T14:47:28.810Z
Learning: In RustPython's stat module implementation, platform-specific constants like SF_SUPPORTED and SF_SYNTHETIC should be conditionally declared only for the platforms where they're available (e.g., macOS), following CPython's approach of optional declaration using #ifdef checks rather than providing fallback values for other platforms.
Applied to files:
crates/vm/src/stdlib/ctypes/util.rs
📚 Learning: 2025-06-27T14:47:28.810Z
Learnt from: moreal
Repo: RustPython/RustPython PR: 5847
File: vm/src/stdlib/stat.rs:547-567
Timestamp: 2025-06-27T14:47:28.810Z
Learning: In RustPython's stat module implementation, platform-specific constants like SF_SUPPORTED and SF_SYNTHETIC should be conditionally declared only for the platforms where they're available (e.g., macOS), following CPython's approach of optional declaration rather than providing fallback values for other platforms.
Applied to files:
crates/vm/src/stdlib/ctypes/util.rs
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
Applied to files:
crates/vm/src/stdlib/ctypes/pointer.rs
🧬 Code graph analysis (5)
crates/vm/src/stdlib/ctypes/base.rs (2)
crates/vm/src/stdlib/ctypes/array.rs (1)
create_array_type_with_stg_info(30-66)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/stdlib/ctypes.rs (3)
crates/vm/src/stdlib/ctypes/function.rs (3)
obj(457-457)t(86-86)t(360-360)crates/vm/src/stdlib/ctypes/field.rs (4)
obj(107-107)obj(116-116)obj(201-201)obj(218-218)crates/vm/src/stdlib/ctypes/array.rs (2)
t(81-81)t(89-89)
crates/vm/src/stdlib/ctypes/util.rs (1)
crates/vm/src/stdlib/ctypes.rs (3)
std(418-418)std(444-444)std(696-696)
crates/vm/src/stdlib/ctypes/structure.rs (2)
crates/vm/src/stdlib/ctypes/array.rs (1)
create_array_type_with_stg_info(30-66)crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
crates/vm/src/stdlib/ctypes/pointer.rs (1)
crates/vm/src/stdlib/ctypes/util.rs (1)
new_array(70-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Run snippets and cpython tests (macos-latest)
- GitHub Check: Run snippets and cpython tests (windows-latest)
- GitHub Check: Run snippets and cpython tests (ubuntu-latest)
- GitHub Check: Check Rust code with clippy
- GitHub Check: Run rust tests (macos-latest)
- GitHub Check: Run rust tests (windows-latest)
- GitHub Check: Run rust tests (ubuntu-latest)
- GitHub Check: Run tests under miri
- GitHub Check: Check the WASM package and demo
- GitHub Check: Ensure compilation on various targets
- GitHub Check: Run snippets and cpython tests on wasm-wasi
🔇 Additional comments (13)
crates/vm/src/stdlib/ctypes/util.rs (1)
55-87: LGTM!The
new()andnew_array()constructors are well-structured. Thenew_array()constructor properly initializes all array-specific fields includingelement_typeandelement_size, which aligns with the new type-data driven approach for ctypes metadata.crates/vm/src/stdlib/ctypes/pointer.rs (2)
12-13: LGTM!The
PyCPointerTypeis now an empty struct with derived traits, consistent with the PR's pattern of moving type metadata totype_datastorage. This simplifies the type while centralizing metadata management.
18-34: LGTM!The
__mul__implementation correctly creates array types using the new factory-based approach. The pointer element size is correctly calculated asstd::mem::size_of::<usize>(), and theStgInfo::new_arraycall properly captures all necessary metadata.crates/vm/src/stdlib/ctypes/base.rs (3)
231-232: LGTM!The
PyCSimpleTypeis now an empty struct, consistent with the PR's pattern of deriving metadata throughget_stg_info()rather than storing it directly.
237-249: LGTM!The
get_stg_infomethod correctly derivesStgInfofrom the_type_attribute, validating that it's a single-character type code before computing size and alignment. The fallback toStgInfo::default()is appropriate for error cases.
743-774: LGTM!The
repeatmethod correctly creates array types using the new factory-based approach. The element size calculation properly handles simple types via their_type_attribute with a sensible fallback to pointer size.crates/vm/src/stdlib/ctypes/union.rs (1)
16-17: LGTM!The
PyCUnionTypeis now an empty struct with derived traits, consistent with the PR's pattern of moving type metadata totype_datastorage. The derivedDefaultis appropriate for the empty struct.crates/vm/src/stdlib/ctypes.rs (2)
394-408: LGTM!The
size_offunction now correctly prioritizesTypeDataSlotfor retrieving size information. The two-step check (first on the instance's class, then on the type object itself) properly handles both instance and type queries, aligning with the new data-driven approach.
670-681: LGTM!The
alignmentfunction follows the same pattern assize_of, correctly prioritizingTypeDataSlotfor alignment information while maintaining backward compatibility with the fallback logic for types that don't use the new type_data mechanism.crates/vm/src/stdlib/ctypes/structure.rs (1)
18-19: LGTM! Struct migration to type_data approach is clean.The migration of PyCStructType to an empty struct with state stored in type_data aligns with the PR's broader refactoring goal and follows the same pattern used in PyCArrayType.
crates/vm/src/stdlib/ctypes/array.rs (3)
29-66: LGTM! Array type factory is well-structured.The
create_array_type_with_stg_infofunction properly creates dynamic array types with StgInfo stored in type_data. Error handling for the downcast is appropriate, and the fallback logic for already-initialized type_data is a good defensive measure.
26-27: LGTM! Type-data accessor pattern is appropriate.The migration to an empty PyCArrayType struct with accessors reading from type_data is clean. The fallback patterns (
unwrap_or_else(|| vm.ctx.none())andunwrap_or(0)) provide reasonable defaults when type_data is missing.Also applies to: 79-92
214-230: LGTM! AsNumber implementation delegates correctly.The
multiplyimplementation properly handles overflow checking and delegates to__mul__. The error handling is appropriate.
| let stg_info = super::util::StgInfo::new_array( | ||
| total_size, | ||
| element_size, | ||
| n as usize, | ||
| cls.clone().into(), | ||
| element_size, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find the StgInfo::new_array definition to confirm signature
rg -nA10 'fn new_array' --type=rust crates/vm/src/stdlib/ctypes/Repository: RustPython/RustPython
Length of output: 747
🏁 Script executed:
# Search for how get_field_align is used
rg -nB2 -A5 'get_field_align' --type=rust crates/vm/src/stdlib/ctypes/Repository: RustPython/RustPython
Length of output: 1568
🏁 Script executed:
# Check structure alignment computation patterns
rg -nC3 'max_align' --type=rust crates/vm/src/stdlib/ctypes/Repository: RustPython/RustPython
Length of output: 3454
🏁 Script executed:
# Look at the full context of __mul__ method and structure initialization
sed -n '150,185p' crates/vm/src/stdlib/ctypes/structure.rsRepository: RustPython/RustPython
Length of output: 1124
🏁 Script executed:
# Check pointer.rs for comparison
rg -nB5 -A10 'StgInfo::new_array' --type=rust crates/vm/src/stdlib/ctypes/pointer.rsRepository: RustPython/RustPython
Length of output: 618
Compute structure alignment instead of using element size for arrays.
The second parameter to StgInfo::new_array should be the structure's alignment, not its element_size. Structure alignment is the maximum alignment of all fields (as computed during structure initialization at line 274), which can differ from the structure's size due to padding.
For arrays of pointers, using element_size is correct because pointer alignment equals pointer size. However, for structure arrays, the alignment should match the structure's computed max_align. Add alignment computation to __mul__:
let max_align = Self::compute_max_align(&cls, vm)?;
let stg_info = super::util::StgInfo::new_array(
total_size,
max_align, // Use computed alignment, not element_size
n as usize,
cls.clone().into(),
element_size,
);🤖 Prompt for AI Agents
In crates/vm/src/stdlib/ctypes/structure.rs around lines 173 to 179, the call to
StgInfo::new_array uses element_size for the array alignment but for arrays of
structures you must pass the structure's computed alignment (max_align) instead
of element_size; compute max_align by calling the existing structure alignment
helper (e.g. Self::compute_max_align(&cls, vm) or the equivalent used at init
time), use that value as the second argument to StgInfo::new_array, and
propagate any error from the alignment computation so the function returns an
Err when alignment lookup fails.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.