Skip to content

__type_params__ in __build_class__ #5883

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

youknowone
Copy link
Member

@youknowone youknowone commented Jul 1, 2025

Summary by CodeRabbit

  • New Features

    • Added support for handling and compiling type parameters in class and function definitions, enabling PEP 695 generics.
    • Type parameters are now accessible within class and function bodies and correctly set as attributes for compatibility.
  • Documentation

    • Added clarifying comments regarding type parameter registration and compilation.

Copy link

coderabbitai bot commented Jul 1, 2025

Walkthrough

The changes add support for handling type parameters (as per PEP 695) in both the compiler and runtime. The compiler now compiles and stores type parameters for classes and functions, while the VM ensures these are available during class body execution and assigns them to the class’s __parameters__ attribute for compatibility.

Changes

File(s) Change Summary
compiler/codegen/src/compile.rs Extended compilation logic to handle, store, and reference type parameters in class/function definitions.
compiler/codegen/src/symboltable.rs Added a clarifying comment in the type parameter scanning method.
vm/src/stdlib/builtins.rs Added logic in __build_class__ to manage type parameters in the class namespace and set __parameters__.

Sequence Diagram(s)

sequenceDiagram
    participant Compiler
    participant VM
    participant Namespace
    participant ClassBody
    participant ClassObject

    Compiler->>Compiler: Compile class/function with type_params
    Compiler->>Compiler: Store type_params in .type_params local
    Compiler->>Compiler: After body, assign to __type_params__
    Compiler->>VM: Emit code referencing .type_params and set TYPE_PARAMS flag

    VM->>VM: __build_class__ called
    VM->>Namespace: If __type_params__ exists, set .type_params in namespace
    VM->>ClassBody: Invoke class body function
    VM->>Namespace: Remove .type_params after execution
    VM->>ClassObject: After class creation, set __parameters__ from __type_params__
Loading

Possibly related PRs

Poem

In the warren where code and type parameters play,
Rabbits compile classes the PEP 695 way.
With .type_params tucked in a local so neat,
And __parameters__ making the class complete.
Now generics hop freely, with nary a fuss—
The code is more magical, thanks to all of us!
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5a33d24 and 358909c.

📒 Files selected for processing (3)
  • compiler/codegen/src/compile.rs (4 hunks)
  • compiler/codegen/src/symboltable.rs (1 hunks)
  • vm/src/stdlib/builtins.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • compiler/codegen/src/symboltable.rs
  • vm/src/stdlib/builtins.rs
  • compiler/codegen/src/compile.rs
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • 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: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check Rust code with rustfmt and clippy
✨ 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

@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/stdlib/builtins.rs (1)

935-948: Implementation correctly handles type parameters for PEP 695 classes.

The code properly retrieves __type_params__ from the function object and makes it available in the namespace during class body execution. Good use of type checking with downcast.

Consider whether silently ignoring the get_attr error is intentional. If __type_params__ is expected but missing, you might want to log or handle this case explicitly.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fa7af0e and 5a33d24.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_typing.py is excluded by !Lib/**
📒 Files selected for processing (3)
  • compiler/codegen/src/compile.rs (4 hunks)
  • compiler/codegen/src/symboltable.rs (1 hunks)
  • vm/src/stdlib/builtins.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.rs`: Follow the default rustfmt code style (`cargo fmt` to format) Always ...

**/*.rs: Follow the default rustfmt code style (cargo fmt to format)
Always run clippy to lint code (cargo clippy) before completing tasks. Fix any warnings or lints that are introduced by your 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

📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • compiler/codegen/src/symboltable.rs
  • vm/src/stdlib/builtins.rs
  • compiler/codegen/src/compile.rs
🧠 Learnings (1)
vm/src/stdlib/builtins.rs (1)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T10:08:48.851Z
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 (11)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
🔇 Additional comments (6)
compiler/codegen/src/symboltable.rs (1)

1270-1270: LGTM! Good documentation addition.

The comment clearly explains the purpose of the subsequent code block.

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

961-965: Proper cleanup of temporary namespace entry.

Good practice to remove the internal .type_params key after use, maintaining CPython compatibility. The use of .ok() is appropriate since the key may not exist in all cases.

compiler/codegen/src/compile.rs (4)

1215-1215: LGTM: Clear documentation improvement.

The added comment helpfully clarifies the purpose of the type parameter compilation process.


1668-1674: LGTM: Correct type parameter compilation and storage.

The implementation properly:

  • Guards the type parameter handling with a conditional check
  • Manages symbol table scope correctly by pushing a new table
  • Reuses existing compile_type_params functionality
  • Uses the well-chosen .type_params variable name to avoid conflicts
  • Employs the appropriate StoreLocal instruction for the scope

1696-1707: LGTM: Correct PEP 695 type_params implementation.

This properly implements the PEP 695 requirement by:

  • Making type parameters available as __type_params__ during class body execution
  • Using LoadNameAny to correctly access .type_params from the enclosing scope
  • Using StoreLocal to properly set the variable in the class body scope
  • Including clear comments explaining the PEP 695 compliance purpose

1741-1745: LGTM: Proper type parameter preparation for class creation.

This correctly implements the final phase of type parameter handling by:

  • Loading the previously compiled .type_params from the appropriate scope
  • Setting the TYPE_PARAMS flag to inform the runtime that type parameters are present
  • Using the correct LoadNameAny instruction for cross-scope access

The implementation ensures type parameters are properly passed to the __build_class__ builtin.

Comment on lines 984 to 988
// For PEP 695 classes, set __parameters__ from __type_params__ (CPython compatibility)
if let Ok(type_params) = class.get_attr(identifier!(vm, __type_params__), vm) {
if let Ok(type_params_tuple) = type_params.clone().downcast::<PyTuple>() {
if !type_params_tuple.is_empty() {
// Extract TypeVar/ParamSpec/TypeVarTuple objects from the tuple
let mut parameters = Vec::new();
for param in type_params_tuple.iter() {
// Check if this is a TypeVar, ParamSpec, or TypeVarTuple
if param.get_attr(identifier!(vm, __name__), vm).is_ok() {
parameters.push(param.clone());
}
}
if !parameters.is_empty() {
let parameters_tuple = PyTuple::new_ref(parameters, &vm.ctx);
class.set_attr(identifier!(vm, __parameters__), parameters_tuple, vm)?;
}
}
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Correctly implements __parameters__ attribute for PEP 695 compatibility.

The code properly extracts type parameter objects and sets them as the __parameters__ attribute on the class, matching CPython's behavior.

The code uses the presence of __name__ attribute to identify TypeVar/ParamSpec/TypeVarTuple objects. While this should work correctly for objects coming from __type_params__, let's verify this matches CPython's exact behavior:


🌐 Web query:

How does CPython determine which objects from __type_params__ should be included in __parameters__ for PEP 695 generic classes?

💡 Result:

In CPython, when implementing PEP 695 for generic classes, the __type_params__ attribute is used to store the type parameters that parameterize the class. This attribute is a tuple containing instances of TypeVar, TypeVarTuple, and ParamSpec. The __parameters__ attribute, on the other hand, is a tuple that includes the type parameters that are directly used in the class's method signatures.

The determination of which objects from __type_params__ should be included in __parameters__ is based on the usage of these type parameters within the class's methods. Specifically, if a type parameter from __type_params__ is used in the signature of a method (for example, as a type annotation for a method parameter or return type), it is included in __parameters__. This inclusion ensures that the method signatures accurately reflect the types that the class is parameterized with, maintaining consistency and clarity in the class's interface.

This approach aligns with the goals of PEP 695, which aims to simplify the syntax for defining generic classes and to make the relationship between a class and its type parameters more explicit and intuitive. By clearly associating type parameters with method signatures, developers can more easily understand and utilize generic classes in their code.


Implementation of __parameters__ needs to filter by method‐signature usage

The CPython logic for PEP 695 only includes type parameters in __parameters__ if they actually appear in the class’s method signatures (as annotations). Simply checking for a __name__ attribute will pull in all TypeVar/ParamSpec/TypeVarTuple instances, not just those used on methods.

Please update the block in
vm/src/stdlib/builtins.rs (lines 984–1002)
so that, after collecting candidates from __type_params__, you further scan the class’s methods’ __annotations__ and include only those parameters that appear there.

[e.g.]

-    // heuristic: has __name__
-    if param.get_attr(__name__, vm).is_ok() {
-        parameters.push(param.clone());
-    }
+    // only include if this TypeVar/ParamSpec/TypeVarTuple
+    // is referenced in any method signature annotation:
+    if class
+        .get_methods()
+        .iter()
+        .any(|func| func.annotations().contains(&param)) 
+    {
+        parameters.push(param.clone());
+    }

— adjust to match your VM’s API for iterating methods and inspecting annotations.

This will ensure feature parity with CPython’s PEP 695 implementation.

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

🤖 Prompt for AI Agents
In vm/src/stdlib/builtins.rs around lines 984 to 1002, the current code sets
__parameters__ by including all TypeVar/ParamSpec/TypeVarTuple objects from
__type_params__ without filtering by actual usage in method signatures. To fix
this, after collecting candidate parameters from __type_params__, iterate over
the class’s methods and inspect their __annotations__ to identify which
parameters are actually used. Then filter the collected parameters to include
only those found in these annotations before setting __parameters__. Use your
VM’s API to access methods and their annotations for this filtering step.

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