-
Notifications
You must be signed in to change notification settings - Fork 17
engine: complete Expr3 pipeline migration, remove lower_late #133
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
Conversation
This commit completes the cleanup tasks for array pass 1: 1. Unified lowering pipeline: lower() now goes through pass 0 -> Expr3 -> pass 1 -> lower_from_expr3, returning Vec<Expr> to support temp array assignments. All call sites updated. 2. Removed Expr3 -> Expr2 conversion: lower_from_expr3 now handles all Expr3 variants directly (Var, Op1, Op2, If, App, Subscript) without converting back to Expr2. 3. Deleted ~800 lines of legacy code: lower_late, convert_expr3_to_expr2, convert_index_expr3_to_expr2, convert_builtin_expr3_to_expr2, old SubscriptConfig/normalize_subscripts, and get_dimension_name_subscript. 4. Fixed ArrayBounds for Range subscripts: Added compute_range_size and expr_to_index helpers to calculate the actual range size (e.g., 3:5 gives size 3, not the full dimension size 5). This was causing incorrect temp array sizing and index out of bounds errors. 5. Cleaned up dead_code annotations: Removed annotations from Expr3 types and methods that are now used. Kept annotations with comments for items needed in pass 2 (StaticSubscript, TempArrayElement, references_a2a_dimension). The net result is ~570 fewer lines of code while maintaining full test coverage (383 tests pass). The codebase is now simpler and more maintainable for implementing pass 2 and other array features.
|
@codex review |
Code Review: PR #133 - Complete Expr3 Pipeline MigrationSummaryThis PR successfully completes the migration to the Expr3 pipeline by removing ~570 lines of legacy code while maintaining full test coverage (383 tests pass). The changes unify the lowering pipeline and fix a critical bug in array bounds calculation for range subscripts. ✅ Strengths1. Excellent Code Cleanup
2. Unified Pipeline Architecture Returning 3. Critical Bug Fix fn compute_range_size(start: &Expr2, end: &Expr2, dim: &Dimension) -> Option<usize> {
let start_idx = Self::expr_to_index(start, dim)?;
let end_idx = Self::expr_to_index(end, dim)?;
// Range is inclusive on both ends, so size is end - start + 1
if end_idx >= start_idx {
Some(end_idx - start_idx + 1)
} else {
None // Invalid range (end before start)
}
}4. Direct Variant Handling 🔍 Issues and Concerns1. Integer Overflow Risk let idx = *val as isize - 1;
if idx >= 0 { Some(idx as usize) } else { None }Problem: Casting Recommendation: Add bounds checking: if *val >= 1.0 && *val <= isize::MAX as f64 {
Some((*val as isize - 1) as usize)
} else {
None
}2. Potential Panic Points
Recommendation: According to CLAUDE.md: "Strongly prefer 3. Code Duplication Recommendation: Consider extracting this to a helper method like 4. Missing Edge Case Handling Recommendation: This could hide bugs. Consider returning an error for invalid ranges instead of silently accepting them. 5. Comment Clarity 🎯 Performance ConsiderationsPositive:
Concern:
🔒 Security ConsiderationsNo security concerns identified. Array bounds are properly validated throughout. 🧪 Test CoverageThe PR mentions 383 tests pass, which is excellent. However: Recommendation: Add specific regression tests for the range bounds bug fix (e.g., test that 📝 Code QualityPositive:
Nitpicks:
✨ Final VerdictStrong Approve - This is high-quality refactoring work that:
The concerns raised are mostly about defensive programming and code clarity rather than correctness issues. The unwrap() calls are acceptable given the project's development philosophy, though adding comments explaining invariants would help future maintainers. Action Items (Optional Improvements)
Great work on this cleanup! The codebase is measurably better after this PR. |
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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Expr::AssignTemp(id, inner, view) => { | ||
| let size = view.dims.iter().product::<usize>(); | ||
| temp_sizes_map.insert(*id, size); | ||
| extract_temp_sizes(inner, temp_sizes_map); |
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.
Preserve max temp size per reused temp id
Because Pass1Context is created per lower() call, temp IDs restart at 0 for every expression, so the same temp ID is reused across many runlist entries. This insert overwrites any prior size for that ID instead of keeping the maximum, meaning a later, smaller temp view can shrink temp_sizes. When an earlier (larger) expression runs, AssignTemp will write past the allocated temp buffer and panic or corrupt results. Consider tracking max per temp ID rather than overwriting.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #133 +/- ##
==========================================
+ Coverage 68.89% 72.90% +4.00%
==========================================
Files 41 41
Lines 12352 12072 -280
==========================================
+ Hits 8510 8801 +291
+ Misses 3842 3271 -571 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
❌ Visual regression tests failed. View artifacts to see the differences. |
1. Fix temp size tracking: Preserve max size per reused temp ID. Since Pass1Context resets temp IDs to 0 for each lower() call, the same ID can be reused across expressions. Use entry().and_modify().or_insert() to keep the maximum size, preventing buffer overflow when a larger expression runs after a smaller one was processed. 2. Fix integer overflow: Guard against f64->isize overflow in expr_to_index by checking val is in range [1, isize::MAX]. 3. Add subscript_to_index helper: Extract verbose dimension-to-index conversion logic into a reusable helper method with clear documentation explaining that indexed dimensions store 1-based indices as strings. 4. Improve compute_range_size documentation: Clarify that None is returned for both non-constant bounds and invalid ranges, with the latter being validated later in build_view_from_ops.
Code Review for PR #133This is a substantial refactoring that successfully completes the Expr3 pipeline migration. The overall approach is sound and the code quality is good. Below are my findings organized by category. Strengths
Potential Issues1. Return type change requires careful migration (Medium Priority) The debug_assert!(!exprs.is_empty(), "lower() must return at least one expression");
let main_expr = exprs.pop().unwrap();2. Overflow handling inconsistency (Low Priority) In 3. Potential panic in subscript_to_index (Medium Priority) In 4. Dead code annotations with comments (Low Priority) Several items have Code Quality Observations
Minor Issues
Security and Performance
RecommendationApprove with minor suggestions. This is solid work that genuinely improves the codebase. The issues raised are mostly edge cases and defensive improvements rather than blocking problems. The test suite passing (383 tests) gives good confidence. The key suggestion is to add defensive assertions around the Vec unwrap operations and consider documenting the integer conversion behavior for indices. Great work on this refactoring! The ~570 line reduction with zero test regressions is impressive. |
|
❌ Visual regression tests failed. View artifacts to see the differences. |
This commit completes the cleanup tasks for array pass 1:
Unified lowering pipeline: lower() now goes through pass 0 -> Expr3
-> pass 1 -> lower_from_expr3, returning Vec to support temp
array assignments. All call sites updated.
Removed Expr3 -> Expr2 conversion: lower_from_expr3 now handles all
Expr3 variants directly (Var, Op1, Op2, If, App, Subscript) without
converting back to Expr2.
Deleted ~800 lines of legacy code: lower_late, convert_expr3_to_expr2,
convert_index_expr3_to_expr2, convert_builtin_expr3_to_expr2, old
SubscriptConfig/normalize_subscripts, and get_dimension_name_subscript.
Fixed ArrayBounds for Range subscripts: Added compute_range_size and
expr_to_index helpers to calculate the actual range size (e.g., 3:5
gives size 3, not the full dimension size 5). This was causing
incorrect temp array sizing and index out of bounds errors.
Cleaned up dead_code annotations: Removed annotations from Expr3
types and methods that are now used. Kept annotations with comments
for items needed in pass 2 (StaticSubscript, TempArrayElement,
references_a2a_dimension).
The net result is ~570 fewer lines of code while maintaining full test
coverage (383 tests pass). The codebase is now simpler and more
maintainable for implementing pass 2 and other array features.