BE-615: HashQL: Introduce the concept of synthetic closures in the MIR#8894
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryMedium Risk Overview Administrative reduction gains Pipeline Smaller related changes: Reviewed by Cursor Bugbot for commit 489dd1a. Bugbot is set up for automated code reviews on this repo. Configure here. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## bm/be-607-hashql-remove-smallvec-from-the-standardlibrary-for-module #8894 +/- ##
====================================================================================================
Coverage 3.62% 3.62%
====================================================================================================
Files 478 478
Lines 14463 14463
Branches 3020 3020
====================================================================================================
Hits 524 524
Misses 13894 13894
Partials 45 45
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
5d51d92 to
f62be24
Compare
4ad5098 to
74017c2
Compare
| // In this case it may either be a forwarding closure **or** a synthetic closure, this | ||
| // depends on the final statement, in either case the last statement must be an assignment. | ||
| let StatementKind::Assign(Assign { lhs, ref rhs }) = final_stmt.kind else { |
| | RValue::Load(_) => Self::TrivialClosure, | ||
| }; | ||
|
|
||
| // Terminator must return the call's result. |
f62be24 to
558d30c
Compare
74017c2 to
410fa1b
Compare
410fa1b to
ac8ed9e
Compare
558d30c to
d08cc92
Compare
| impl DefId { | ||
| /// Built-in dictionary insert operation (immutable). | ||
| /// | ||
| /// This operation inserts a key-value pair into a dictionary, | ||
| /// returning a new dictionary with the added pair. The original | ||
| /// dictionary remains unchanged. | ||
| pub const DICT_INSERT: Self = Self::new(0xFFFF_FE00); | ||
| /// Built-in dictionary insert operation (mutable). | ||
| /// | ||
| /// This operation inserts a key-value pair into a dictionary in-place, | ||
| /// modifying the original dictionary. Used for efficient dictionary | ||
| /// construction and updates. | ||
| pub const DICT_INSERT_MUT: Self = Self::new(0xFFFF_FE01); | ||
| /// Built-in dictionary remove operation (immutable). | ||
| /// | ||
| /// This operation removes a key-value pair from a dictionary, | ||
| /// returning a new dictionary without the specified key. The | ||
| /// original dictionary remains unchanged. | ||
| pub const DICT_REMOVE: Self = Self::new(0xFFFF_FE02); | ||
| /// Built-in dictionary remove operation (mutable). | ||
| /// | ||
| /// This operation removes a key-value pair from a dictionary in-place, | ||
| /// modifying the original dictionary and returning the removed value | ||
| /// if the key existed. | ||
| pub const DICT_REMOVE_MUT: Self = Self::new(0xFFFF_FE03); | ||
| /// Built-in list pop operation (immutable). | ||
| /// | ||
| /// This operation removes the last element from a list, returning | ||
| /// both the element and a new list without the element. The original | ||
| /// list remains unchanged. | ||
| pub const LIST_POP: Self = Self::new(0xFFFF_FE04); | ||
| /// Built-in list pop operation (mutable). | ||
| /// | ||
| /// This operation removes the last element from a list in-place, | ||
| /// returning the removed element while modifying the original list. | ||
| pub const LIST_POP_MUT: Self = Self::new(0xFFFF_FE05); | ||
| /// Built-in list push operation (immutable). | ||
| /// | ||
| /// This operation appends an element to a list, returning a new list | ||
| /// without modifying the original. Used for functional-style list | ||
| /// manipulation where immutability is preferred. | ||
| pub const LIST_PUSH: Self = Self::new(0xFFFF_FE06); | ||
| /// Built-in list push operation (mutable). | ||
| /// | ||
| /// This operation appends an element to a list in-place, modifying | ||
| /// the original list. Used for imperative-style list manipulation | ||
| /// where performance is critical. | ||
| pub const LIST_PUSH_MUT: Self = Self::new(0xFFFF_FE07); | ||
| pub const PLACEHOLDER: Self = Self::MAX; | ||
| } |
| pub(in crate::module::std_lib) mod bits; | ||
| pub(in crate::module::std_lib) mod bool; | ||
| pub(in crate::module::std_lib) mod cmp; | ||
| pub(in crate::module::std_lib) mod json; | ||
| pub mod json; | ||
| pub(in crate::module::std_lib) mod math; | ||
| pub mod option; | ||
| pub(in crate::module::std_lib) mod result; |
d08cc92 to
5014ae0
Compare
ac8ed9e to
4aacfd8
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4aacfd8. Configure here.
5014ae0 to
8c7419a
Compare
4aacfd8 to
f2b45cb
Compare
| // In this case it may either be a forwarding closure **or** a synthetic closure, this | ||
| // depends on the final statement, in either case the last statement must be an assignment. | ||
| let StatementKind::Assign(Assign { lhs, ref rhs }) = final_stmt.kind else { | ||
| return None; | ||
| }; | ||
|
|
||
| // The kind depends on the final statement. Both cases are beta-reduction | ||
| // (substituting arguments into a function body at a call site): | ||
| // | ||
| // Apply: the body forwards to another call. The wrapper (lambda x. f(x)) is an | ||
| // eta-expansion of the inner function; inlining at the call site is beta-reduction | ||
| // that exposes the inner call directly. | ||
| // | ||
| // Binary, Unary, and other primitive operations: the body computes a single result | ||
| // from its arguments. Inlining substitutes the arguments and exposes the primitive | ||
| // operation. A subsequent constant folding pass may then perform delta-reduction | ||
| // (evaluating the primitive when operands are known). | ||
| let kind = match rhs { | ||
| RValue::Apply(_) => Self::ForwardingClosure, | ||
| RValue::Aggregate(_) | ||
| | RValue::Binary(_) | ||
| | RValue::Unary(_) | ||
| | RValue::Input(_) | ||
| | RValue::Load(_) => Self::TrivialClosure, | ||
| }; |
| if bb.terminator.kind | ||
| == TerminatorKind::Return(Return { | ||
| value: Operand::Place(lhs), | ||
| }) | ||
| { |
| // `property<T>(entity: Entity<T>, path: JsonPath) -> ?` | ||
| // TODO(BE-62): return `Option<?>` once pattern matching allows for option destructuring, to | ||
| // allow for proper comparison | ||
| let decl = decl!(context; | ||
| <T>(entity: context.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), | ||
| path: json_path_ty.id | ||
| ) -> option(&context.ty, context.ty.unknown()) | ||
| ) -> context.ty.unknown() | ||
| ); |
f2b45cb to
489dd1a
Compare
8c7419a to
4abefab
Compare


🌟 What is the purpose of this PR?
Introduces
Source::Syntheticfor compiler-generated wrapper bodies and extends administrative reduction to handle single-operation function bodies (TrivialClosure).This is groundwork for making intrinsics usable in value position (e.g. passing
+as an argument to a higher-order function). TheSyntheticsource variant will identify wrapper bodies that bridge non-first-class operations into callable closures, andTrivialClosureensures they are always inlined away.🔍 What does this change?
Source::Synthetic(Symbol)to the MIRSourceenum for compiler-synthesized operation wrappersReductionKind::TrivialClosureto administrative reduction, recognizing single-basic-block bodies where a trivial prelude leads to a single non-call operation (Binary,Unary,Aggregate,Input,Load) whose result is returnedSyntheticbodies getInlineDirective::Always(same asCtor)Sourcematches updated: execution pipeline, statement placement, eval context, pretty printer[synthetic sym::path]source to thebody!test macroThe
TrivialClosureclassification is intentionally broader than just synthetic bodies. Any function matching the shape is reducible, which unlocks optimization cascades: single-operation closures get inlined, exposing constant-foldable expressions, enabling branch elimination downstream.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
🐾 Next steps
qualified_pathin reification to generate synthetic wrapper bodies for intrinsics in value position🛡 What tests cover this?
classify_trivial_closure_binary,classify_trivial_closure_unary,classify_non_reducible_non_trivial_preludeinline_trivial_closure_binary,inline_trivial_closure_transitivenested-branch-eliminationnow demonstrates the full optimization cascade)❓ How to test this?
cargo test --package hashql-mir --lib -- administrative_reduction::tests📹 Demo
The
nested-branch-eliminationcompiletest shows the cascade effect: closures containing single comparisons (> 100,< 0) are now inlined by AR, exposing constant-foldable comparisons (50 > 100,50 < 0), which enables the entire branch structure to collapse toreturn "in range".