Skip to content

Conversation

@ShaharNaveh
Copy link
Collaborator

@ShaharNaveh ShaharNaveh commented Dec 3, 2025

Summary by CodeRabbit

  • Refactor
    • Improved compiler internals for safer symbol-table handling to reduce crashes and make lookup behavior explicit.
  • Bug Fixes
    • Replaced fragile index-based lookups with guarded checks that return clear syntax errors when state is inconsistent.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Replaced direct indexing and panic-prone accesses on the symbol-table stack in crates/codegen/src/compile.rs with guarded accessors (last(), get(), expect()/unwrap_last()), and added explicit SyntaxError returns for missing entries instead of relying on out-of-bounds panics.

Changes

Cohort / File(s) Summary
Symbol table safety refactor
crates/codegen/src/compile.rs
Replaced explicit indexing into symbol_table_stack with safe accessors: current_symbol_table now uses last().expect(), push_symbol_table and callers use unwrap_last()/current_symbol_table() to obtain the new top, and lookups in enter_scope/parent retrievals use get(key) with explicit SyntaxError on missing entries.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Check current_symbol_table, push_symbol_table, and enter_scope for correct guarded access and clear error messages.
  • Verify all call sites replaced index-based access consistently.
  • Confirm no remaining unchecked indexing or unintended unwraps.

Possibly related PRs

Poem

🐰 I nudge the stack with gentle paws,

No sudden panics, only laws,
I check each hop, I ask and see,
"Is there a table? — yes, or flee."
Hop safe, hop sound — compile with glee! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: refactoring vector access patterns in codegen/src/compile.rs to use Rust idioms (last(), get(), etc.) instead of unsafe indexing.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e24221b and 83c1ff1.

📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/codegen/src/compile.rs
⏰ 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: Ensure compilation on various targets
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Check Rust code with clippy
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Check the WASM package and demo

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 0

🧹 Nitpick comments (2)
crates/codegen/src/compile.rs (2)

609-630: Avoid unsafe + unwrap_unchecked() in push_symbol_table

The invariant (“we just pushed, so it can't be empty”) is correct, but unsafe { last().unwrap_unchecked() } is unnecessary here and makes the code harder to audit. A safe version is just as concise and keeps the same “compiler bug” semantics:

-        self.symbol_table_stack.push(table);
-        // SAFETY: We just pushed, so it can't be empty
-        unsafe { &self.symbol_table_stack.last().unwrap_unchecked() }
+        self.symbol_table_stack.push(table);
+        self.symbol_table_stack
+            .last()
+            .expect("symbol_table_stack is empty after push! This is a compiler bug.")

This also matches the style used in current_symbol_table().


1192-1210: Make TypeParams parent lookup avoid len() - 2 underflow

In the TypeParams special case, the parent table is fetched via:

self.symbol_table_stack
    .get(self.symbol_table_stack.len() - 2)
    .expect("Symbol has no parent! This is a compiler bug.")
    .lookup(name.as_ref())

Given current invariants, len() >= 2 whenever is_typeparams is true, but if that invariant is ever broken, the code will panic earlier with an attempt to subtract with overflow instead of the intended “compiler bug” message.

You can keep the same semantics while avoiding this sharp edge and using more idiomatic traversal:

-            let symbol = if symbol.is_none() && is_typeparams {
-                self.symbol_table_stack
-                    .get(self.symbol_table_stack.len() - 2) // Try to get parent index
-                    .expect("Symbol has no parent! This is a compiler bug.")
-                    .lookup(name.as_ref())
-            } else {
-                symbol
-            };
+            let symbol = if symbol.is_none() && is_typeparams {
+                let parent = self
+                    .symbol_table_stack
+                    .iter()
+                    .rev()
+                    .nth(1)
+                    .expect("Symbol has no parent! This is a compiler bug.");
+                parent.lookup(name.as_ref())
+            } else {
+                symbol
+            };

This keeps the “compiler bug” panic but removes the intermediate len() - 2 arithmetic.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f49c185 and ee7377e.

📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to 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/codegen/src/compile.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 : Follow Rust best practices for error handling and memory management

Applied to files:

  • crates/codegen/src/compile.rs
⏰ 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: Ensure compilation on various targets
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • 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: Check Rust code with clippy
  • GitHub Check: Run tests under miri
🔇 Additional comments (2)
crates/codegen/src/compile.rs (2)

558-563: current_symbol_table now uses idiomatic Vec::last access

Using symbol_table_stack.last().expect(...) is clearer and avoids manual indexing while still treating an empty stack as an internal compiler bug. No issues here.


657-665: enter_scope symbol-table lookup now uses Vec::get safely

Switching from manual index-based access to self.symbol_table_stack.get(key) with an explicit SyntaxError("unknown symbol table entry") on None is a good, idiomatic improvement and keeps out-of-bounds conditions from turning into generic panics.

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: 0

🧹 Nitpick comments (2)
crates/codegen/src/compile.rs (2)

612-629: Avoid unsafe / unwrap_unchecked in push_symbol_table

The unwrap_unchecked here is correct under the stated invariant, but it introduces unsafe for a very small gain. Since this isn’t an ultra‑hot path and the stack cannot be empty after push, you can keep the code safe and aligned with the PR’s intent (idiomatic Vec access) by reusing current_symbol_table instead of unsafe:

         let table = current_table.sub_tables.remove(0);

         // Push the next table onto the stack
         self.symbol_table_stack.push(table);
-        // SAFETY: We just pushed, so it can't be empty
-        unsafe { self.symbol_table_stack.last().unwrap_unchecked() }
+        self.current_symbol_table()

This preserves behavior, removes unsafe, and keeps all Vec access in one helper. As per coding guidelines, safer patterns are preferred when possible.


1193-1204: Make parent lookup in TypeParams scope avoid len() - 2 arithmetic

The TypeParams parent lookup now uses Vec::get(self.symbol_table_stack.len() - 2).expect(...). Logically this matches the existing invariant (“there must be a parent”), but len() - 2 can underflow in debug builds if that invariant is ever broken, yielding a less helpful panic than your custom expect message.

You can stay within the “idiomatic Vec access” goal and avoid manual index math by using an iterator from the back to get “the element before last”:

-            // If not found and we're in TypeParams scope, try parent scope
-            let symbol = if symbol.is_none() && is_typeparams {
-                self.symbol_table_stack
-                    .get(self.symbol_table_stack.len() - 2) // Try to get parent index
-                    .expect("Symbol has no parent! This is a compiler bug.")
-                    .lookup(name.as_ref())
-            } else {
-                symbol
-            };
+            // If not found and we're in TypeParams scope, try parent scope
+            let symbol = if symbol.is_none() && is_typeparams {
+                let parent = self
+                    .symbol_table_stack
+                    .iter()
+                    .rev()
+                    .nth(1) // second-from-last element is the parent
+                    .expect("Symbol has no parent! This is a compiler bug.");
+                parent.lookup(name.as_ref())
+            } else {
+                symbol
+            };

This keeps the same invariant but avoids explicit len() - 2 arithmetic on the Vec.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee7377e and e24221b.

📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to 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/codegen/src/compile.rs
🧠 Learnings (1)
📓 Common learnings
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 : Follow Rust best practices for error handling and memory management
⏰ 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 on wasm-wasi
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Check Rust code with clippy
  • GitHub Check: Run tests under miri
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
🔇 Additional comments (2)
crates/codegen/src/compile.rs (2)

560-562: current_symbol_table now uses Vec::last with a clear invariant

Using .last().expect("symbol_table_stack is empty! ...") is an idiomatic way to express the internal invariant and avoids manual indexing. Looks good.


658-664: Guarded enter_scope symbol-table lookup is clear and safe

Switching from direct indexing to self.symbol_table_stack.get(key) with an explicit SyntaxError("unknown symbol table entry") on None makes this more robust and self-documenting, while keeping the invariant that callers must pass a valid key. No issues here.

Copy link
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

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

👍

@youknowone youknowone merged commit 2b90e82 into RustPython:main Dec 4, 2025
13 checks passed
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.

2 participants