Skip to content

Integrate PyTupleTyped into PyTuple #5959

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

Merged
merged 4 commits into from
Jul 14, 2025
Merged

Conversation

youknowone
Copy link
Member

@youknowone youknowone commented Jul 13, 2025

Summary by CodeRabbit

  • Refactor

    • Improved internal consistency and type safety for tuple handling and closure representation in built-in functions and type objects.
    • Updated several public struct fields and method signatures to use reference-counted tuple types.
    • Enhanced payload type identification and trait requirements for object payloads.
    • Adjusted import-related methods to use references for tuple arguments, improving borrowing semantics.
    • Relaxed trait bounds on generic Python object wrappers for more flexible usage.
    • Simplified traversal trait bounds on internal object representations.
    • Refined error handling in type conversion for Python references.
    • Added utility method to obtain an empty typed tuple from the context.
    • Modified generic trait bounds for generated traversal implementations to include necessary constraints on type parameters.
    • Updated function closure handling to use reference-counted typed tuples with safe conversions.
    • Refined typed tuple abstractions to unify internal representations and conversions between typed and untyped tuples.
    • Adjusted import and function execution logic to use typed tuple references instead of owned tuples.
  • New Features

    • Added a standardized method for retrieving payload type identifiers for Python objects.
  • Style

    • Refined trait implementations and type parameterization for improved code clarity and maintainability.

Copy link
Contributor

coderabbitai bot commented Jul 13, 2025

Walkthrough

The changes refactor the handling of typed Python tuples and related generics throughout the codebase. The PyTupleTyped struct is removed and replaced by a generic PyTuple<R> parameterized over element type, using PyRef<T> payloads. Conversion logic is updated to use reference-counted pointers (PyRef) for tuple elements. This affects function closures, type slots, and import operations, with corresponding updates to trait bounds, type conversions, and trait implementations for payloads and traversal. Additional changes introduce a standardized method for obtaining payload type IDs and enforce stricter trait bounds for object payloads.

Changes

File(s) Change Summary
derive-impl/src/pytraverse.rs Modified impl_pytraverse to add Traverse trait bounds to generics and update generated code to support generic types with traversal.
vm/src/builtins/function.rs Changed closure field and related logic in PyFunction to use Option<PyRef<PyTuple<PyCellRef>>> instead of Option<PyTupleTyped<PyCellRef>>; updated constructors and conversion methods accordingly.
vm/src/builtins/tuple.rs Refactored PyTuple to be generic over element type R; removed PyTupleTyped; added safe conversions between typed and untyped tuples using PyRef<T>; updated trait implementations and methods accordingly.
vm/src/builtins/type.rs Changed slots field in HeapTypeExt and related code to use Option<PyRef<PyTuple<PyStrRef>>> instead of Option<PyTupleTyped<PyStrRef>>.
vm/src/frame.rs Updated stack handling for imports and function creation to use PyRef<PyTuple<PyStrRef>> and PyRef<PyTuple<PyCellRef>> for tuples; adjusted type extraction and conversion logic.
vm/src/object/core.rs Relaxed trait bounds on Py<T>, PyRef<T>, and related impls; updated payload_is method to use T::payload_type_id() for type checking instead of TypeId::of::<T>().
vm/src/object/payload.rs Added payload_type_id() to PyPayload trait; expanded PyObjectPayload trait bounds to require PyPayload.
vm/src/object/traverse_object.rs Changed Traverse implementation bound on PyInner<T> from T: PyObjectPayload to T: MaybeTraverse.
vm/src/vm/context.rs Added empty_tuple_typed method returning a typed empty tuple reference via unsafe transmute.
vm/src/vm/mod.rs Changed import functions to take from_list as a reference &Py<PyTuple<PyStrRef>>; updated internal usage and conversions.
vm/src/convert/try_from.rs Refactored error handling in try_from_object for PyRef<T> to consolidate error mapping via map_err.

Sequence Diagram(s)

sequenceDiagram
    participant Frame as ExecutingFrame
    participant VM as VirtualMachine
    participant Tuple as PyTuple<PyRef<T>>
    participant Function as PyFunction

    Frame->>Frame: pop_value() for closure or from_list
    Frame->>Tuple: PyRef::<PyTuple<PyRef<...>>>::try_from_object(...)
    Frame->>Function: PyFunction::new(..., closure: Option<PyRef<PyTuple<PyCellRef>>>)
    Frame->>VM: import_from(..., from_list: &Py<PyTuple<PyRef<PyStrRef>>>, ...)
    VM->>Tuple: Context::empty_tuple_typed()
    VM->>VM: import_inner(..., from_list: &Py<PyTuple<PyRef<PyStrRef>>>, ...)
Loading

Possibly related PRs

  • Pyfunction builtins and constructor #5823: Changes to the PyFunction struct and its constructor, including handling of the closure field and constructor logic, closely relate to the modifications in vm/src/builtins/function.rs in this PR.

Poem

🐇
Refreshed are the tuples, now typed and neat,
With PyRefs they gather, no bugs to defeat.
Closures and slots, all wrapped up with care,
Imports more polished, with types everywhere.
Traversing the fields, our code hops along—
In the meadow of Python, all types now belong!

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (2)
vm/src/frame.rs (1)

1349-1355: Good refactor aligning with PyRef pattern, but consider error handling improvement.

The changes correctly implement the new PyRef<PyTupleTyped<PyStrRef>> pattern and improve code clarity by separating the pop operation from the conversion. The reference passing to import_from is also consistent with the broader refactor.

Consider making the empty tuple handling more explicit:

-        let from_list = <Option<PyRef<PyTupleTyped<PyStrRef>>>>::try_from_object(vm, top)?
-            .unwrap_or_else(|| PyTupleTyped::<PyStrRef>::empty(&vm.ctx).to_owned());
+        let from_list = <Option<PyRef<PyTupleTyped<PyStrRef>>>>::try_from_object(vm, top)?
+            .unwrap_or_else(|| PyTupleTyped::<PyStrRef>::empty(&vm.ctx).to_owned());

This makes the ownership transfer of the empty tuple more explicit and matches the pattern used elsewhere in the codebase.

vm/src/builtins/tuple.rs (1)

561-573: Consider relaxing trait bounds on empty() method.

The trait bounds T: fmt::Debug + Send + Sync + 'static seem unnecessary for an empty tuple since there are no elements to satisfy these constraints. These bounds might unnecessarily restrict usage of the empty tuple.

Consider removing or relaxing these bounds:

-    pub fn empty(ctx: &Context) -> &Py<Self>
-    where
-        T: fmt::Debug + Send + Sync + 'static,
-    {
+    pub fn empty(ctx: &Context) -> &Py<Self> {
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ab7aa2 and a52fc91.

📒 Files selected for processing (8)
  • derive-impl/src/pytraverse.rs (1 hunks)
  • vm/src/builtins/function.rs (4 hunks)
  • vm/src/builtins/tuple.rs (3 hunks)
  • vm/src/builtins/type.rs (3 hunks)
  • vm/src/frame.rs (2 hunks)
  • vm/src/object/core.rs (1 hunks)
  • vm/src/object/payload.rs (2 hunks)
  • vm/src/vm/mod.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .github/copilot-instructions.md
🧠 Learnings (4)
derive-impl/src/pytraverse.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
vm/src/builtins/function.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
vm/src/frame.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
vm/src/builtins/tuple.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
🧬 Code Graph Analysis (2)
vm/src/object/core.rs (2)
vm/src/builtins/tuple.rs (1)
  • payload_type_id (515-517)
vm/src/object/payload.rs (1)
  • payload_type_id (23-25)
vm/src/builtins/tuple.rs (3)
vm/src/object/payload.rs (1)
  • payload_type_id (23-25)
vm/src/object/core.rs (4)
  • TypeId (451-451)
  • traverse (127-129)
  • traverse (135-137)
  • Self (373-376)
vm/src/object/traverse.rs (1)
  • try_traverse (17-17)
⏰ 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). (9)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Ensure compilation on various targets
🔇 Additional comments (23)
vm/src/object/core.rs (1)

657-659: LGTM! Type identification standardization implemented correctly.

The change from TypeId::of::<T>() to T::payload_type_id() properly utilizes the new standardized method from the PyPayload trait. This centralizes type identity checks and aligns with the broader refactoring to unify payload type handling across the VM.

derive-impl/src/pytraverse.rs (2)

108-117: LGTM! Proper trait bounds added for generic traversal.

The automatic addition of Traverse bounds to all generic type parameters ensures that the derived Traverse implementation is valid for generic types. The use of split_for_impl() correctly handles the generics splitting for the macro-generated code.


120-120: Correctly applies generics and bounds to the implementation.

The generated unsafe impl block now properly includes the generics and where clause, ensuring type safety for the derived traversal implementations.

vm/src/object/payload.rs (2)

22-25: LGTM! Standardized type ID method implemented correctly.

The new payload_type_id() method provides a centralized and standardized way to obtain type identifiers for payload types. The inline annotation is appropriate for this frequently-used method.


82-82: Proper trait hierarchy established.

Expanding PyObjectPayload to require PyPayload ensures that all object payloads provide the standardized payload_type_id() method, creating a consistent trait hierarchy across the VM.

vm/src/builtins/type.rs (4)

65-65: LGTM! Reference-counted tuple field type updated correctly.

The change from Option<PyTupleTyped<PyStrRef>> to Option<PyRef<PyTupleTyped<PyStrRef>>> aligns with the broader refactoring to use reference-counted pointers for tuple elements, improving type safety and consistency.


1039-1039: Variable type correctly updated to match field type.

The heaptype_slots variable type is properly updated to Option<PyRef<PyTupleTyped<PyStrRef>>> to match the corresponding field type change.


1042-1046: Conversion logic updated for single string slots.

The PyRef::<PyTupleTyped<PyStrRef>>::try_from_object call correctly handles the conversion for single string slots with the new reference-counted tuple type.


1055-1058: Conversion logic updated for iterable slots.

The PyRef::<PyTupleTyped<PyStrRef>>::try_from_object call correctly handles the conversion for iterable slots with the new reference-counted tuple type.

vm/src/frame.rs (1)

1843-1843: Correctly updates closure type to align with PyRef pattern.

The change from PyTupleTyped<PyCellRef> to PyRef<PyTupleTyped<PyCellRef>> is consistent with the broader refactor described in the summary and properly maintains the reference-counted semantics for tuple elements.

vm/src/builtins/function.rs (4)

14-17: LGTM! Import reorganization aligns with module structure.

The relocation of TryFromObject and ToPyObject to the main import block is appropriate for their usage patterns.


35-35: Good architectural change to use reference-counted tuples.

The change from Option<PyTupleTyped<PyCellRef>> to Option<PyRef<PyTupleTyped<PyCellRef>>> aligns with Python's reference-counting semantics and improves memory efficiency by avoiding unnecessary tuple cloning.


61-61: Constructor signature correctly updated to match field type.

The closure parameter type change is consistent with the field declaration, and the usage at line 356 properly handles the reference semantics.

Also applies to: 356-356


616-617: Conversion correctly updated for PyRef-wrapped typed tuples.

The change to use PyRef::<PyTupleTyped<PyCellRef>>::try_from_object properly converts the closure tuple to the expected reference-counted typed tuple format.

vm/src/vm/mod.rs (4)

602-602: Good use of singleton empty tuple pattern.

Using PyTupleTyped::<PyStrRef>::empty(&self.ctx) leverages the singleton pattern for empty tuples, improving memory efficiency.


612-612: Efficient parameter passing with reference instead of owned value.

The change to accept &Py<PyTupleTyped<PyStrRef>> avoids unnecessary cloning and follows Rust's borrowing best practices.


622-622: Parameter type consistently updated in import_inner.

The reference parameter is properly used throughout the method, including the is_empty() check at line 627.

Also applies to: 627-627


660-660: Correct ownership conversion for function call.

The to_owned().to_pyobject(self) properly converts the borrowed reference to an owned PyObjectRef as required by the import function call.

vm/src/builtins/tuple.rs (5)

503-508: Struct refactoring improves type safety and ownership model.

The changes establish a clearer ownership model:

  • Storing PyTuple directly instead of PyTupleRef simplifies the internal representation
  • The generic parameter R with TransmuteFromObject bound ensures type safety for transmutation
  • The safety invariant comment properly documents the transmutation requirements

510-522: Well-designed PyPayload implementation for type identity.

The implementation correctly:

  • Restricts PyPayload to PyTupleTyped<PyRef<T>> ensuring type safety
  • Returns PyTuple's type ID for proper type identity checks
  • Maintains compatibility with the Python type system

524-538: Correct garbage collection traversal implementation.

The Traverse and MaybeTraverse implementations properly handle GC traversal by:

  • Delegating to the underlying tuple's traverse method
  • Setting IS_TRACE = true to ensure typed tuples are always traced
  • Adding appropriate trait bounds for type safety

554-558: AsRef implementation correctly updated for PyRef elements.

The change to return &[PyRef<T>] aligns with the new type structure. The unsafe cast in as_slice() is justified by the transmutation safety requirements documented in the struct.

Also applies to: 577-577


597-602: Clean conversion trait implementation.

The From trait implementation provides a convenient and safe way to convert typed tuple references back to regular tuple references using the into_untyped() method.

@youknowone youknowone force-pushed the tuple branch 2 times, most recently from 0261ba2 to fff5446 Compare July 13, 2025 14:27
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
vm/src/builtins/tuple.rs (1)

516-521: Add missing #[repr(transparent)] attribute for safe transmutation.

The PyTupleTyped struct uses unsafe transmute operations throughout the code but lacks the #[repr(transparent)] attribute that is required for these operations to be sound.

+#[repr(transparent)]
 pub struct PyTupleTyped<R> {
     // SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
     //                   elements must be logically valid when transmuted to T
     tuple: PyTuple,
     _marker: PhantomData<R>,
 }
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0261ba2 and fff5446.

📒 Files selected for processing (9)
  • vm/src/builtins/function.rs (5 hunks)
  • vm/src/builtins/tuple.rs (4 hunks)
  • vm/src/builtins/type.rs (3 hunks)
  • vm/src/convert/try_from.rs (1 hunks)
  • vm/src/frame.rs (3 hunks)
  • vm/src/object/core.rs (10 hunks)
  • vm/src/object/traverse_object.rs (2 hunks)
  • vm/src/vm/context.rs (2 hunks)
  • vm/src/vm/mod.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • vm/src/object/traverse_object.rs
  • vm/src/vm/context.rs
  • vm/src/convert/try_from.rs
  • vm/src/frame.rs
  • vm/src/builtins/type.rs
  • vm/src/vm/mod.rs
  • vm/src/builtins/function.rs
  • vm/src/object/core.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .github/copilot-instructions.md
🧠 Learnings (1)
vm/src/builtins/tuple.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
🧬 Code Graph Analysis (1)
vm/src/builtins/tuple.rs (2)
vm/src/object/traverse.rs (1)
  • try_traverse (17-17)
vm/src/convert/transmute_from.rs (2)
  • check (13-13)
  • check (17-28)
⏰ 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: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run tests under miri
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Ensure compilation on various targets
🔇 Additional comments (4)
vm/src/builtins/tuple.rs (4)

6-6: LGTM! Import added for new trait implementation.

The MaybeTraverse import is correctly added to support the new trait implementation in the code.


523-537: LGTM! Trait implementations correctly simplified.

The Traverse implementation is properly constrained with R: TransmuteFromObject, and the new MaybeTraverse implementation correctly delegates to the Traverse implementation when both traits are available.


577-598: LGTM! Trait implementations and methods are correctly implemented.

The AsRef trait implementation and the accessor methods (as_slice, len, is_empty) are properly implemented using safe operations that delegate to the underlying tuple.


600-611: LGTM! Debug and From implementations are correctly implemented.

The Debug implementation correctly delegates to the tuple's slice formatting, and the From trait implementation properly uses the safe into_untyped method.

Comment on lines 539 to 555
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
unsafe {
let elements: Vec<PyObjectRef> =
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
let tuple = PyTuple::new_ref(elements, ctx);
tuple.into_typed_unchecked::<T>()
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Verify transmute safety and add documentation.

The new_ref constructor uses unsafe transmute to convert between Vec<PyRef<T>> and Vec<PyObjectRef>. While this should be safe given that PyRef<T> should have the same representation as PyObjectRef, it needs proper safety documentation.

 impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
+    /// Creates a new typed tuple from a vector of typed references.
+    /// 
+    /// # Safety
+    /// 
+    /// This method relies on `PyRef<T>` having the same memory layout as `PyObjectRef`.
+    /// The transmute is safe because `PyRef<T>` is a thin wrapper around `PyObjectRef`
+    /// with compile-time type guarantees.
     pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
         unsafe {
+            // SAFETY: PyRef<T> has the same layout as PyObjectRef
             let elements: Vec<PyObjectRef> =
                 std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
             let tuple = PyTuple::new_ref(elements, ctx);
             tuple.into_typed_unchecked::<T>()
         }
     }
 }
📝 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.

Suggested change
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
unsafe {
let elements: Vec<PyObjectRef> =
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
let tuple = PyTuple::new_ref(elements, ctx);
tuple.into_typed_unchecked::<T>()
}
}
}
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
/// Creates a new typed tuple from a vector of typed references.
///
/// # Safety
///
/// This method relies on `PyRef<T>` having the same memory layout as `PyObjectRef`.
/// The transmute is safe because `PyRef<T>` is a thin wrapper around `PyObjectRef`
/// with compile-time type guarantees.
pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
unsafe {
// SAFETY: `PyRef<T>` has the same layout as `PyObjectRef`
let elements: Vec<PyObjectRef> =
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
let tuple = PyTuple::new_ref(elements, ctx);
tuple.into_typed_unchecked::<T>()
}
}
}
🤖 Prompt for AI Agents
In vm/src/builtins/tuple.rs around lines 539 to 548, the use of unsafe transmute
to convert Vec<PyRef<T>> to Vec<PyObjectRef> requires explicit safety
documentation. Add a comment explaining why this transmute is safe, specifically
that PyRef<T> and PyObjectRef have the same memory representation, and ensure
the reasoning is clear to maintainers. This will clarify the safety assumptions
behind the unsafe block.

Comment on lines 550 to 578
impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
pub fn into_pyobject(self) -> PyObjectRef {
self.into_untyped().into()
}

pub fn into_untyped(self) -> PyRef<PyTuple> {
// SAFETY: PyTupleTyped is transparent over PyTuple
unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
}

pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
// Check that all elements are of the correct type
for elem in tuple.as_slice() {
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
}
// SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
}
}

impl<T: PyPayload> Py<PyTupleTyped<PyRef<T>>> {
pub fn as_untyped(&self) -> &Py<PyTuple> {
// SAFETY: PyTupleTyped is transparent over PyTuple
unsafe { std::mem::transmute::<&Py<PyTupleTyped<PyRef<T>>>, &Py<PyTuple>>(self) }
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance safety documentation for transmute operations.

The conversion methods use multiple unsafe transmute operations. While the safety comments are present, they should be more comprehensive to justify the safety of these operations.

 impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
     pub fn into_pyobject(self) -> PyObjectRef {
         self.into_untyped().into()
     }

     pub fn into_untyped(self) -> PyRef<PyTuple> {
-        // SAFETY: PyTupleTyped is transparent over PyTuple
+        // SAFETY: PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple,
+        // so the memory layout is identical and transmute is safe
         unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
     }

     pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
         // Check that all elements are of the correct type
         for elem in tuple.as_slice() {
             <PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
         }
-        // SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
+        // SAFETY: We verified all elements are valid PyRef<T> instances via TransmuteFromObject::check,
+        // and PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple with identical memory layout
         Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
     }
 }

 impl<T: PyPayload> Py<PyTupleTyped<PyRef<T>>> {
     pub fn as_untyped(&self) -> &Py<PyTuple> {
-        // SAFETY: PyTupleTyped is transparent over PyTuple
+        // SAFETY: PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple,
+        // so reference transmutation preserves memory layout and lifetime
         unsafe { std::mem::transmute::<&Py<PyTupleTyped<PyRef<T>>>, &Py<PyTuple>>(self) }
     }
 }
📝 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.

Suggested change
impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
pub fn into_pyobject(self) -> PyObjectRef {
self.into_untyped().into()
}
pub fn into_untyped(self) -> PyRef<PyTuple> {
// SAFETY: PyTupleTyped is transparent over PyTuple
unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
}
pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
// Check that all elements are of the correct type
for elem in tuple.as_slice() {
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
}
// SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
}
}
impl<T: PyPayload> Py<PyTupleTyped<PyRef<T>>> {
pub fn as_untyped(&self) -> &Py<PyTuple> {
// SAFETY: PyTupleTyped is transparent over PyTuple
unsafe { std::mem::transmute::<&Py<PyTupleTyped<PyRef<T>>>, &Py<PyTuple>>(self) }
}
}
impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
pub fn into_pyobject(self) -> PyObjectRef {
self.into_untyped().into()
}
pub fn into_untyped(self) -> PyRef<PyTuple> {
// SAFETY: PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple,
// so the memory layout is identical and transmute is safe
unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
}
pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
// Check that all elements are of the correct type
for elem in tuple.as_slice() {
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
}
// SAFETY: We verified all elements are valid PyRef<T> instances via TransmuteFromObject::check,
// and PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple with identical memory layout
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
}
}
impl<T: PyPayload> Py<PyTupleTyped<PyRef<T>>> {
pub fn as_untyped(&self) -> &Py<PyTuple> {
// SAFETY: PyTupleTyped<PyRef<T>> is repr(transparent) over PyTuple,
// so reference transmutation preserves memory layout and lifetime
unsafe { std::mem::transmute::<&Py<PyTupleTyped<PyRef<T>>>, &Py<PyTuple>>(self) }
}
}
🤖 Prompt for AI Agents
In vm/src/builtins/tuple.rs around lines 550 to 575, the unsafe transmute
operations lack detailed safety justification. Enhance the safety comments by
explicitly explaining why the layout compatibility holds, such as stating that
PyTupleTyped is a transparent wrapper over PyTuple and that the element type
checks guarantee the validity of the transmute. Clarify that these conditions
ensure no undefined behavior occurs during the conversions.

@youknowone youknowone force-pushed the tuple branch 3 times, most recently from 81dc877 to 12ecbbd Compare July 14, 2025 03:40
@youknowone youknowone changed the title PyTupleTyped based on PyTuple Integrate PyTupleTyped into PyTuple Jul 14, 2025
@youknowone youknowone requested a review from coolreader18 July 14, 2025 05:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
vm/src/builtins/tuple.rs (1)

475-487: Enhance safety documentation for try_into_typed.

The transmute operation needs more detailed safety justification, particularly about layout compatibility between the generic types.

 impl PyRef<PyTuple<PyObjectRef>> {
+    /// Attempts to convert an untyped tuple to a typed tuple.
+    /// 
+    /// # Safety
+    /// 
+    /// This method uses transmute after verifying all elements are of type T.
+    /// The transmute is safe because PyTuple<PyRef<T>> has the same memory layout
+    /// as PyTuple<PyObjectRef> when PyRef<T> has the same layout as PyObjectRef.
     pub fn try_into_typed<T: PyPayload>(
         self,
         vm: &VirtualMachine,
     ) -> PyResult<PyRef<PyTuple<PyRef<T>>>> {
         // Check that all elements are of the correct type
         for elem in self.as_slice() {
             <PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
         }
-        // SAFETY: We just verified all elements are of type T
+        // SAFETY: We verified all elements are valid PyRef<T> instances via TransmuteFromObject::check,
+        // and PyTuple<PyRef<T>> has the same memory layout as PyTuple<PyObjectRef>
         Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTuple<PyRef<T>>>>(self) })
     }
 }
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12ecbbd and 6342ad4.

📒 Files selected for processing (11)
  • derive-impl/src/pytraverse.rs (1 hunks)
  • vm/src/builtins/function.rs (7 hunks)
  • vm/src/builtins/tuple.rs (4 hunks)
  • vm/src/builtins/type.rs (5 hunks)
  • vm/src/convert/try_from.rs (1 hunks)
  • vm/src/frame.rs (3 hunks)
  • vm/src/object/core.rs (10 hunks)
  • vm/src/object/payload.rs (2 hunks)
  • vm/src/object/traverse_object.rs (2 hunks)
  • vm/src/vm/context.rs (1 hunks)
  • vm/src/vm/mod.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • vm/src/convert/try_from.rs
  • vm/src/vm/context.rs
  • derive-impl/src/pytraverse.rs
  • vm/src/builtins/type.rs
  • vm/src/vm/mod.rs
  • vm/src/frame.rs
  • vm/src/builtins/function.rs
  • vm/src/object/payload.rs
  • vm/src/object/traverse_object.rs
  • vm/src/object/core.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .github/copilot-instructions.md
🧠 Learnings (1)
vm/src/builtins/tuple.rs (2)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to Lib/test/**/*.py : Add a `# TODO: RUSTPYTHON` comment when modifications are made to tests in `Lib/test`
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.858Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
⏰ 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: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
🔇 Additional comments (6)
vm/src/builtins/tuple.rs (6)

27-29: Generic tuple implementation looks good.

The refactoring to make PyTuple generic over element type R is a clean approach that eliminates the need for a separate wrapper struct.


142-193: Generic trait implementations are correctly updated.

All the trait implementations (AsRef, Deref, IntoIterator) have been properly updated to work with the generic parameter R. The implementations maintain the same semantics while being more flexible.


489-494: Safety comment is adequate for into_untyped.

The transmute operation is properly documented and the safety reasoning is sound.


496-501: Safety comment is adequate for as_untyped.

The reference transmute operation is properly documented with appropriate safety reasoning.


503-508: From trait implementation is correctly implemented.

The conversion from typed to untyped tuple reference is straightforward and reuses the existing into_untyped method.


235-238: Layout compatibility confirmed
Both PyRef<T> and PyObjectRef are #[repr(transparent)] newtypes over a single NonNull<…> pointer (to Py<T> and PyObject, respectively). Since they’re the same size, alignment, and ABI, transmuting Vec<PyRef<T>> to Vec<PyObjectRef> is sound.

Comment on lines +233 to +243
impl<T> PyTuple<PyRef<T>> {
pub fn new_ref_typed(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<PyTuple<PyRef<T>>> {
// SAFETY: PyRef<T> has the same layout as PyObjectRef
unsafe {
let elements: Vec<PyObjectRef> =
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
let tuple = PyTuple::<PyObjectRef>::new_ref(elements, ctx);
std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTuple<PyRef<T>>>>(tuple)
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify double transmutation in new_ref_typed.

The method performs two unsafe transmutes in sequence, which is unnecessarily complex and increases the risk of errors. The conversion can be simplified.

 impl<T> PyTuple<PyRef<T>> {
     pub fn new_ref_typed(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<PyTuple<PyRef<T>>> {
-        // SAFETY: PyRef<T> has the same layout as PyObjectRef
-        unsafe {
-            let elements: Vec<PyObjectRef> =
-                std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
-            let tuple = PyTuple::<PyObjectRef>::new_ref(elements, ctx);
-            std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTuple<PyRef<T>>>>(tuple)
-        }
+        if elements.is_empty() {
+            // SAFETY: Empty tuple conversion is always safe
+            unsafe { std::mem::transmute(ctx.empty_tuple.clone()) }
+        } else {
+            // SAFETY: PyRef<T> has the same layout as PyObjectRef
+            let elements: Box<[PyRef<T>]> = elements.into_boxed_slice();
+            let elements: Box<[PyObjectRef]> = unsafe {
+                std::mem::transmute::<Box<[PyRef<T>]>, Box<[PyObjectRef]>>(elements)
+            };
+            PyRef::new_ref(
+                PyTuple { elements },
+                ctx.types.tuple_type.to_owned(),
+                None,
+            )
+        }
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In vm/src/builtins/tuple.rs around lines 233 to 243, the new_ref_typed method
uses two unsafe transmute calls in sequence, which is overly complex and
error-prone. Simplify this by performing a single transmute or by restructuring
the code to avoid double transmutation, ensuring the conversion from
Vec<PyRef<T>> to Vec<PyObjectRef> and from PyRef<PyTuple> to
PyRef<PyTuple<PyRef<T>>> is done safely and clearly in one step.

@youknowone youknowone merged commit eef8890 into RustPython:main Jul 14, 2025
12 checks passed
@youknowone youknowone deleted the tuple branch July 14, 2025 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant