diff --git a/libs/@local/hashql/mir/src/body/mod.rs b/libs/@local/hashql/mir/src/body/mod.rs index 74d229d8a35..65001060ff9 100644 --- a/libs/@local/hashql/mir/src/body/mod.rs +++ b/libs/@local/hashql/mir/src/body/mod.rs @@ -77,6 +77,16 @@ pub enum Source<'heap> { /// The body of an intrinsic function is typically empty, as the intrinsic /// operation is handled directly by the compiler or runtime. Intrinsic(DefId), + + /// A filter closure for graph read operations. + /// + /// This variant represents MIR generated from a closure expression used to + /// filter entities during a graph read operation. The [`HirId`] identifies + /// the filter closure expression in the HIR. + /// + /// Filter bodies are never inlined and are executed during graph traversal + /// to determine which entities should be included in the read results. + GraphReadFilter(HirId), } /// The MIR body of a HashQL function or query. diff --git a/libs/@local/hashql/mir/src/pass/analysis/callgraph/mod.rs b/libs/@local/hashql/mir/src/pass/analysis/callgraph/mod.rs index 1708668af22..84a76ca1ab1 100644 --- a/libs/@local/hashql/mir/src/pass/analysis/callgraph/mod.rs +++ b/libs/@local/hashql/mir/src/pass/analysis/callgraph/mod.rs @@ -182,19 +182,6 @@ impl CallGraph<'_, A> { }) } - #[inline] - pub fn filters(&self) -> impl Iterator { - self.inner - .nodes() - .ids() - .filter(|&node| { - self.inner - .incoming_edges(node) - .any(|edge| matches!(edge.data, CallKind::Filter(_))) - }) - .map(|node| DefId::new(node.as_u32())) - } - #[inline] pub fn is_leaf(&self, def: DefId) -> bool { let def = NodeId::new(def.as_usize()); diff --git a/libs/@local/hashql/mir/src/pass/transform/inline/analysis.rs b/libs/@local/hashql/mir/src/pass/transform/inline/analysis.rs index dadcbb77b38..3d1ad00def9 100644 --- a/libs/@local/hashql/mir/src/pass/transform/inline/analysis.rs +++ b/libs/@local/hashql/mir/src/pass/transform/inline/analysis.rs @@ -274,7 +274,7 @@ impl<'ctx, 'heap, A: Allocator> BodyAnalysis<'ctx, 'heap, A> { let inline = match body.source { Source::Ctor(_) => InlineDirective::Always, Source::Closure(_, _) | Source::Thunk(_, _) => InlineDirective::Heuristic, - Source::Intrinsic(_) => InlineDirective::Never, + Source::Intrinsic(_) | Source::GraphReadFilter(_) => InlineDirective::Never, }; // Detect loops using SCC analysis on the CFG. diff --git a/libs/@local/hashql/mir/src/pass/transform/inline/mod.rs b/libs/@local/hashql/mir/src/pass/transform/inline/mod.rs index 374fc085226..5867f489049 100644 --- a/libs/@local/hashql/mir/src/pass/transform/inline/mod.rs +++ b/libs/@local/hashql/mir/src/pass/transform/inline/mod.rs @@ -74,7 +74,7 @@ use self::{ }; use crate::{ body::{ - Body, + Body, Source, basic_block::{BasicBlock, BasicBlockId}, local::{Local, LocalDecl}, location::Location, @@ -523,8 +523,10 @@ impl Inline { } let mut filters = DenseBitSet::new_empty(bodies.len()); - for filter in graph.filters() { - filters.insert(filter); + for body in bodies { + if matches!(body.source, Source::GraphReadFilter(_)) { + filters.insert(body.id); + } } let costs = analysis.finish(); diff --git a/libs/@local/hashql/mir/src/pretty/text.rs b/libs/@local/hashql/mir/src/pretty/text.rs index b6ec518fc03..d1732724995 100644 --- a/libs/@local/hashql/mir/src/pretty/text.rs +++ b/libs/@local/hashql/mir/src/pretty/text.rs @@ -33,7 +33,10 @@ use crate::{ const fn source_keyword(source: Source<'_>) -> &'static str { match source { Source::Thunk(..) => "thunk", - Source::Ctor(_) | Source::Closure(..) | Source::Intrinsic(_) => "fn", + Source::Ctor(_) + | Source::Closure(..) + | Source::GraphReadFilter(..) + | Source::Intrinsic(_) => "fn", } } @@ -272,6 +275,7 @@ where write!(self.writer, "{{ctor#{symbol}}}") } Source::Closure(id, binder) => named_symbol("closure", id, binder), + Source::GraphReadFilter(id) => named_symbol("graph::read::filter", id, None), Source::Thunk(id, binder) => named_symbol("thunk", id, binder), Source::Intrinsic(def_id) => { write!(self.writer, "{{intrinsic#{def_id}}}") diff --git a/libs/@local/hashql/mir/src/reify/mod.rs b/libs/@local/hashql/mir/src/reify/mod.rs index 885f22c16b2..c37e2416eb5 100644 --- a/libs/@local/hashql/mir/src/reify/mod.rs +++ b/libs/@local/hashql/mir/src/reify/mod.rs @@ -183,9 +183,12 @@ impl<'ctx, 'mir, 'hir, 'env, 'heap> Reifier<'ctx, 'mir, 'hir, 'env, 'heap> { // In the future we might want to specialize `ctor` in a way that allows us to move them to // be thin calls (although that would require that we move functions into a separate type // from closures). - let env = if matches!(source, Source::Closure(_, _) | Source::Ctor(_)) { + let env = if matches!( + source, + Source::Closure(_, _) | Source::Ctor(_) | Source::GraphReadFilter(_) + ) { let r#type = if let Some((_, type_id)) = captures { - debug_assert_matches!(source, Source::Closure(..)); + debug_assert_matches!(source, Source::Closure(..) | Source::GraphReadFilter(..)); type_id } else { // In case there are no captures, the environment will always be a unit type (aka @@ -289,16 +292,16 @@ impl<'ctx, 'mir, 'hir, 'env, 'heap> Reifier<'ctx, 'mir, 'hir, 'env, 'heap> { fn lower_closure( self, hir: HirPtr, + source: Source<'heap>, captures: &MixedBitSet, env_type: TypeId, - binder: Option>, closure: Closure<'heap>, closure_type: ClosureType<'heap>, ) -> DefId { debug_assert_eq!(closure_type.params.len(), closure.signature.params.len()); self.lower_impl( - Source::Closure(hir.id, binder), + source, hir.span, closure .signature diff --git a/libs/@local/hashql/mir/src/reify/rvalue.rs b/libs/@local/hashql/mir/src/reify/rvalue.rs index df4a5665dbd..2c811a3b07e 100644 --- a/libs/@local/hashql/mir/src/reify/rvalue.rs +++ b/libs/@local/hashql/mir/src/reify/rvalue.rs @@ -22,6 +22,7 @@ use super::{ }; use crate::{ body::{ + Source, constant::Constant, local::Local, operand::Operand, @@ -291,7 +292,7 @@ impl<'mir, 'heap> Reifier<'_, 'mir, '_, '_, 'heap> { r#type: _, value: env, }, - ) = self.transform_closure(block, hir, Some(binder), closure); + ) = self.transform_closure(block, hir, Source::Closure(hir.id, Some(binder)), closure); // We first need to figure out the environment that we need to capture, these are variables // that are referenced out of scope (upvars). diff --git a/libs/@local/hashql/mir/src/reify/terminator.rs b/libs/@local/hashql/mir/src/reify/terminator.rs index 6e0a1915819..cfba67fb9d3 100644 --- a/libs/@local/hashql/mir/src/reify/terminator.rs +++ b/libs/@local/hashql/mir/src/reify/terminator.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::{ body::{ + Source, basic_block::BasicBlockId, local::Local, terminator::{ @@ -56,7 +57,12 @@ impl<'mir, 'heap> Reifier<'_, 'mir, '_, '_, 'heap> { return GraphReadBody::Filter(DefId::MAX, Local::MAX); }; - let (ptr, env) = self.transform_closure(block, filter.ptr(), None, closure); + let (ptr, env) = self.transform_closure( + block, + filter.ptr(), + Source::GraphReadFilter(filter.ptr().id), + closure, + ); GraphReadBody::Filter(ptr.value, env.value) } } diff --git a/libs/@local/hashql/mir/src/reify/transform.rs b/libs/@local/hashql/mir/src/reify/transform.rs index d0d0f0695c2..a3249dca65a 100644 --- a/libs/@local/hashql/mir/src/reify/transform.rs +++ b/libs/@local/hashql/mir/src/reify/transform.rs @@ -10,7 +10,7 @@ use hashql_hir::{ HirPtr, Node, closure::Closure, kind::NodeKind, - r#let::{Binder, Binding, Let}, + r#let::{Binding, Let}, }, visit::Visitor as _, }; @@ -18,6 +18,7 @@ use hashql_hir::{ use super::{Reifier, current::CurrentBlock, error::local_variable_unmapped, unwrap_closure_type}; use crate::{ body::{ + Source, local::{Local, LocalDecl}, operand::Operand, place::Place, @@ -32,7 +33,7 @@ impl<'mir, 'heap> Reifier<'_, 'mir, '_, '_, 'heap> { &mut self, block: &mut CurrentBlock<'mir, 'heap>, hir: HirPtr, - binder: Option>, + source: Source<'heap>, closure: Closure<'heap>, ) -> (Typed, Typed) { let mut dependencies = VariableDependencies::from_set(self.state.var_pool.acquire()); @@ -80,7 +81,7 @@ impl<'mir, 'heap> Reifier<'_, 'mir, '_, '_, 'heap> { let closure_type_id = self.context.hir.map.monomorphized_type_id(hir.id); let closure_type = unwrap_closure_type(closure_type_id, self.context.mir.env); let compiler = Reifier::new(self.context, self.state); - let ptr = compiler.lower_closure(hir, &captures, env_type, binder, closure, closure_type); + let ptr = compiler.lower_closure(hir, source, &captures, env_type, closure, closure_type); block.push_statement(Statement { span: hir.span, diff --git a/libs/@local/hashql/mir/tests/ui/pass/data-dependency/graph-read-filter.stdout b/libs/@local/hashql/mir/tests/ui/pass/data-dependency/graph-read-filter.stdout index 9c47766ca00..040e4c2ebad 100644 --- a/libs/@local/hashql/mir/tests/ui/pass/data-dependency/graph-read-filter.stdout +++ b/libs/@local/hashql/mir/tests/ui/pass/data-dependency/graph-read-filter.stdout @@ -1,6 +1,6 @@ ════ MIR ═══════════════════════════════════════════════════════════════════════ -fn {closure@11}(%0: (Boolean,), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@11}(%0: (Boolean,), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean bb0(): { @@ -29,7 +29,7 @@ fn {closure@11}(%0: (Boolean,), %1: ::graph::types::knowledge::entity::Entity) - %5 = (%3) graph read entities(%2) - |> filter({closure@11}, %5) + |> filter({graph::read::filter@11}, %5) |> collect -> bb2(_) } diff --git a/libs/@local/hashql/mir/tests/ui/pass/inline/filter-aggressive.stdout b/libs/@local/hashql/mir/tests/ui/pass/inline/filter-aggressive.stdout index 1132d09c95f..ec3eaf48466 100644 --- a/libs/@local/hashql/mir/tests/ui/pass/inline/filter-aggressive.stdout +++ b/libs/@local/hashql/mir/tests/ui/pass/inline/filter-aggressive.stdout @@ -10,7 +10,7 @@ thunk {thunk#1}() -> ::graph::TimeAxis { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean bb0(): { @@ -30,7 +30,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } @@ -51,7 +51,7 @@ thunk {thunk#1}() -> ::graph::TimeAxis { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean bb0(): { @@ -71,7 +71,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } @@ -92,7 +92,7 @@ thunk {thunk#1}() -> ::graph::TimeAxis { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean bb0(): { @@ -120,7 +120,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } diff --git a/libs/@local/hashql/mir/tests/ui/pass/inline/filter-with-ctor.stdout b/libs/@local/hashql/mir/tests/ui/pass/inline/filter-with-ctor.stdout index 0c0ffba04ae..639ab3f9cd8 100644 --- a/libs/@local/hashql/mir/tests/ui/pass/inline/filter-with-ctor.stdout +++ b/libs/@local/hashql/mir/tests/ui/pass/inline/filter-with-ctor.stdout @@ -76,7 +76,7 @@ thunk {thunk#5}() -> ::graph::types::knowledge::entity::EntityUuid { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: ::graph::types::knowledge::entity::EntityUuid let %3: Boolean @@ -98,7 +98,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } @@ -181,7 +181,7 @@ thunk {thunk#5}() -> ::graph::types::knowledge::entity::EntityUuid { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean let %3: ::core::uuid::Uuid let %4: ::graph::types::knowledge::entity::EntityUuid @@ -205,7 +205,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } @@ -288,7 +288,7 @@ thunk {thunk#5}() -> ::graph::types::knowledge::entity::EntityUuid { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: Boolean let %3: ::core::uuid::Uuid let %4: ::graph::types::knowledge::entity::EntityUuid @@ -320,7 +320,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) } diff --git a/libs/@local/hashql/mir/tests/ui/reify/graph-read.stdout b/libs/@local/hashql/mir/tests/ui/reify/graph-read.stdout index ddaecd6d5ae..f3dd0cdef82 100644 --- a/libs/@local/hashql/mir/tests/ui/reify/graph-read.stdout +++ b/libs/@local/hashql/mir/tests/ui/reify/graph-read.stdout @@ -74,7 +74,7 @@ thunk {thunk#5}() -> ::graph::types::knowledge::entity::EntityUuid { } } -fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { +fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean { let %2: ::graph::types::knowledge::entity::EntityUuid let %3: Boolean @@ -96,7 +96,7 @@ fn {closure@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity) -> Boolean %2 = () graph read entities(%0) - |> filter({closure@7}, %2) + |> filter({graph::read::filter@7}, %2) |> collect -> bb1(_) }