diff --git a/libs/@local/graph/api/src/rest/entity_query_request.rs b/libs/@local/graph/api/src/rest/entity_query_request.rs index 4d96c3bdb4b..a4710c5a028 100644 --- a/libs/@local/graph/api/src/rest/entity_query_request.rs +++ b/libs/@local/graph/api/src/rest/entity_query_request.rs @@ -14,6 +14,10 @@ //! //! When changing any of these types, make sure that the OpenAPI generator types do not degenerate //! into any of these cases. +#![expect( + dead_code, + reason = "https://linear.app/hash/issue/BE-537/hashql-remove-old-backend-wire-up-hashql-in-the-api" +)] use alloc::borrow::Cow; use core::{cmp, ops::Range}; @@ -41,23 +45,17 @@ use hash_graph_store::{ }; use hashql_ast::error::AstDiagnosticCategory; use hashql_core::{ - collections::fast_hash_map_with_capacity, heap::Heap, - module::ModuleRegistry, span::{SpanId, SpanTable}, - r#type::environment::Environment, }; use hashql_diagnostics::{ - DiagnosticIssues, Failure, Severity, Status, StatusExt as _, Success, + DiagnosticIssues, Failure, Severity, category::{DiagnosticCategory, canonical_category_id}, diagnostic::render::{Format, RenderOptions}, - source::{DiagnosticSpan, Source, SourceId, Sources}, -}; -use hashql_eval::{ - error::EvalDiagnosticCategory, - graph::{error::GraphCompilerDiagnosticCategory, read::FilterSlice}, + source::{DiagnosticSpan, Source, Sources}, }; -use hashql_hir::{error::HirDiagnosticCategory, visit::Visitor as _}; +use hashql_eval::error::EvalDiagnosticCategory; +use hashql_hir::error::HirDiagnosticCategory; use hashql_syntax_jexpr::{error::JExprDiagnosticCategory, span::Span}; use http::StatusCode; use serde::Deserialize; @@ -329,92 +327,6 @@ pub enum EntityQuery<'q> { } impl<'q> EntityQuery<'q> { - fn compile_query<'heap>( - heap: &'heap Heap, - spans: &mut SpanTable, - query: &RawJsonValue, - ) -> Status, HashQLDiagnosticCategory, SpanId> { - // Parse the query - let mut parser = hashql_syntax_jexpr::Parser::new(heap, spans); - let mut ast = parser - .parse_expr(query.get().as_bytes()) - .map_err(|diagnostic| { - Failure::new(diagnostic.map_category(HashQLDiagnosticCategory::JExpr)) - })?; - - let mut env = Environment::new(heap); - let modules = ModuleRegistry::new(&env); - - // Lower the AST - let Success { - value: types, - advisories, - } = hashql_ast::lowering::lower(heap.intern_symbol("main"), &mut ast, &env, &modules) - .map_category(|category| { - HashQLDiagnosticCategory::Ast(AstDiagnosticCategory::Lowering(category)) - })?; - - let interner = hashql_hir::intern::Interner::new(heap); - let mut context = hashql_hir::context::HirContext::new(&interner, &modules); - - // Reify the HIR from the AST - let Success { - value: hir, - advisories, - } = hashql_hir::node::NodeData::from_ast(ast, &mut context, &types) - .map_category(|category| { - HashQLDiagnosticCategory::Hir(HirDiagnosticCategory::Reification(category)) - }) - .with_diagnostics(advisories)?; - - // Lower the HIR - let Success { - value: hir, - advisories, - } = hashql_hir::lower::lower(hir, &types, &mut env, &mut context) - .map_category(|category| { - HashQLDiagnosticCategory::Hir(HirDiagnosticCategory::Lowering(category)) - }) - .with_diagnostics(advisories)?; - - // Evaluate the HIR - // TODO: https://linear.app/hash/issue/BE-41/hashql-expose-input-in-graph-api - let inputs = fast_hash_map_with_capacity(0); - let mut compiler = hashql_eval::graph::read::GraphReadCompiler::new(heap, &inputs); - - compiler.visit_node(hir); - - let Success { - value: result, - advisories, - } = compiler - .finish() - .map_category(|category| { - HashQLDiagnosticCategory::Eval(EvalDiagnosticCategory::Graph( - GraphCompilerDiagnosticCategory::Read(category), - )) - }) - .with_diagnostics(advisories)?; - - let output = result.output.get(&hir.id).expect("TODO"); - - // Compile the Filter into one - let filters = match output { - FilterSlice::Entity { range } => result.filters.entity(range.clone()), - }; - - let filter = match filters { - [] => Filter::All(Vec::new()), - [filter] => filter.clone(), - _ => Filter::All(filters.to_vec()), - }; - - Ok(Success { - value: filter, - advisories, - }) - } - /// Compiles a query into an executable entity filter. /// /// Transforms the query representation into a [`Filter`] that can be executed @@ -427,35 +339,14 @@ impl<'q> EntityQuery<'q> { /// Returns an error if the HashQL query cannot be compiled. pub(crate) fn compile( self, - heap: &'q Heap, - options: CompilationOptions, + _: &'q Heap, + _: CompilationOptions, ) -> Result, BoxedResponse> { match self { EntityQuery::Filter { filter } => Ok(filter), - EntityQuery::Query { query } => { - let mut spans = SpanTable::new(SourceId::new_unchecked(0x00)); - - let Success { - value: filter, - advisories, - } = Self::compile_query(heap, &mut spans, query).map_err(|failure| { - failure_to_response(failure, query.get(), &spans, options) - })?; - if !advisories.is_empty() { - // This isn't perfect, what we'd want instead is to return it alongside the - // response, the problem with that approach is just how: we'd need to adjust the - // return type, and respect interactive. Returning warnings before so that user - // can fix them before trying again seems to be the best approach for now. - return Err(issues_to_response( - advisories.generalize(), - Severity::Warning, - query.get(), - &spans, - options, - )); - } - - Ok(filter) + EntityQuery::Query { query: _ } => { + let response = (StatusCode::NOT_IMPLEMENTED, "https://linear.app/hash/issue/BE-537/hashql-remove-old-backend-wire-up-hashql-in-the-api").into_response(); + Err(response.into()) } } } diff --git a/libs/@local/hashql/compiletest/src/suite/eval_postgres.rs b/libs/@local/hashql/compiletest/src/suite/eval_postgres.rs index 7cd0bd37246..f38539e2592 100644 --- a/libs/@local/hashql/compiletest/src/suite/eval_postgres.rs +++ b/libs/@local/hashql/compiletest/src/suite/eval_postgres.rs @@ -7,7 +7,7 @@ use hashql_core::{ r#type::{TypeFormatter, TypeFormatterOptions, environment::Environment}, }; use hashql_diagnostics::DiagnosticIssues; -use hashql_eval::{context::EvalContext, postgres::PostgresCompiler}; +use hashql_eval::{context::CodeGenerationContext, postgres::PostgresCompiler}; use hashql_mir::{ body::{Body, basic_block::BasicBlockId, terminator::TerminatorKind}, context::MirContext, @@ -117,7 +117,8 @@ impl Suite for EvalPostgres { let mir_buf = format_mir_with_placement(heap, &environment, &bodies, &analysis); secondary_outputs.insert("mir", mir_buf); - let mut context = EvalContext::new_in( + let interner = interner.into(); + let mut context = CodeGenerationContext::new_in( &environment, &interner, &bodies, diff --git a/libs/@local/hashql/core/src/graph/linked.rs b/libs/@local/hashql/core/src/graph/linked.rs index 6494bd62ad1..1b5d19d6784 100644 --- a/libs/@local/hashql/core/src/graph/linked.rs +++ b/libs/@local/hashql/core/src/graph/linked.rs @@ -138,11 +138,13 @@ impl HasId for Edge { impl Edge { /// Returns the source node of this edge. + #[inline] pub const fn source(&self) -> NodeId { self.source } /// Returns the target node of this edge. + #[inline] pub const fn target(&self) -> NodeId { self.target } @@ -542,11 +544,9 @@ impl Default for LinkedGraph { } } -impl core::fmt::Debug - for LinkedGraph -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LinkedGraph") +impl fmt::Debug for LinkedGraph { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("LinkedGraph") .field("nodes", &self.nodes) .field("edges", &self.edges) .finish() diff --git a/libs/@local/hashql/eval/src/context.rs b/libs/@local/hashql/eval/src/context.rs index dd7c7bd15d4..de7df9e75fa 100644 --- a/libs/@local/hashql/eval/src/context.rs +++ b/libs/@local/hashql/eval/src/context.rs @@ -1,4 +1,4 @@ -use core::{alloc::Allocator, ops::Index}; +use core::{alloc::Allocator, fmt, ops::Index}; use hashql_core::{ heap::BumpAllocator, id::bit_vec::DenseBitSet, r#type::environment::Environment, @@ -11,7 +11,6 @@ use hashql_mir::{ local::Local, }, def::{DefId, DefIdSlice, DefIdVec}, - intern::Interner, pass::{ analysis::dataflow::{ TraversalLivenessAnalysis, @@ -21,7 +20,7 @@ use hashql_mir::{ }, }; -use crate::error::EvalDiagnosticIssues; +use crate::{error::EvalDiagnosticIssues, intern::Interner}; struct BasicBlockLiveOut( Box, TraversalPathBitSet)>, A>, @@ -49,7 +48,7 @@ impl Index<(DefId, BasicBlockId)> for LiveOut { } } -pub struct EvalContext<'ctx, 'heap, A: Allocator> { +pub struct CodeGenerationContext<'ctx, 'heap, A: Allocator> { pub env: &'ctx Environment<'heap>, pub interner: &'ctx Interner<'heap>, @@ -58,10 +57,11 @@ pub struct EvalContext<'ctx, 'heap, A: Allocator> { pub live_out: LiveOut, pub diagnostics: EvalDiagnosticIssues, + pub alloc: A, } -impl<'ctx, 'heap, A: Allocator> EvalContext<'ctx, 'heap, A> { +impl<'ctx, 'heap, A: Allocator> CodeGenerationContext<'ctx, 'heap, A> { pub fn new_in( env: &'ctx Environment<'heap>, interner: &'ctx Interner<'heap>, @@ -121,3 +121,39 @@ impl<'ctx, 'heap, A: Allocator> EvalContext<'ctx, 'heap, A> { } } } + +#[derive(Copy, Clone)] +pub struct CodeExecutionContext<'ctx, 'heap, A: Allocator> { + pub env: &'ctx Environment<'heap>, + pub interner: &'ctx Interner<'heap>, + + pub bodies: &'ctx DefIdSlice>, + pub execution: &'ctx DefIdSlice>>, + + pub alloc: A, +} + +impl fmt::Debug for CodeExecutionContext<'_, '_, A> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("CodeExecutionContext") + .field("env", &self.env) + .field("interner", &self.interner) + .field("bodies", &self.bodies) + .field("execution", &self.execution) + .finish_non_exhaustive() + } +} + +impl<'ctx, 'heap, A: Allocator> From> + for CodeExecutionContext<'ctx, 'heap, A> +{ + fn from(context: CodeGenerationContext<'ctx, 'heap, A>) -> Self { + Self { + env: context.env, + interner: context.interner, + bodies: context.bodies, + execution: context.execution, + alloc: context.alloc, + } + } +} diff --git a/libs/@local/hashql/eval/src/error.rs b/libs/@local/hashql/eval/src/error.rs index bdfc6b5e556..91388d645fc 100644 --- a/libs/@local/hashql/eval/src/error.rs +++ b/libs/@local/hashql/eval/src/error.rs @@ -3,18 +3,17 @@ use alloc::borrow::Cow; use hashql_core::span::SpanId; use hashql_diagnostics::{Diagnostic, DiagnosticIssues, Severity, category::DiagnosticCategory}; -#[cfg(feature = "graph")] -use crate::graph::error::GraphCompilerDiagnosticCategory; -use crate::postgres::error::PostgresDiagnosticCategory; +use crate::{ + orchestrator::OrchestratorDiagnosticCategory, postgres::error::PostgresDiagnosticCategory, +}; pub type EvalDiagnostic = Diagnostic; pub type EvalDiagnosticIssues = DiagnosticIssues; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum EvalDiagnosticCategory { - #[cfg(feature = "graph")] - Graph(GraphCompilerDiagnosticCategory), Postgres(PostgresDiagnosticCategory), + Orchestrator(OrchestratorDiagnosticCategory), } impl DiagnosticCategory for EvalDiagnosticCategory { @@ -28,9 +27,8 @@ impl DiagnosticCategory for EvalDiagnosticCategory { fn subcategory(&self) -> Option<&dyn DiagnosticCategory> { match self { - #[cfg(feature = "graph")] - Self::Graph(graph) => Some(graph), Self::Postgres(postgres) => Some(postgres), + Self::Orchestrator(orchestrator) => Some(orchestrator), } } } diff --git a/libs/@local/hashql/eval/src/intern.rs b/libs/@local/hashql/eval/src/intern.rs new file mode 100644 index 00000000000..dee91f141df --- /dev/null +++ b/libs/@local/hashql/eval/src/intern.rs @@ -0,0 +1,28 @@ +use hashql_core::{intern::InternSet, symbol::Symbol}; + +/// Interner for the evaluation stage. +/// +/// Must be created from the MIR interner via [`From`] to preserve +/// [`Interned`](hashql_core::intern::Interned) pointer identity across +/// the MIR/eval boundary. +#[derive(Debug)] +pub struct Interner<'heap> { + pub symbols: InternSet<'heap, [Symbol<'heap>]>, +} + +#[cfg(test)] +impl<'heap> Interner<'heap> { + pub(crate) fn testing(heap: &'heap hashql_core::heap::Heap) -> Self { + Self { + symbols: InternSet::new(heap), + } + } +} + +impl<'heap> From> for Interner<'heap> { + fn from(interner: hashql_mir::intern::Interner<'heap>) -> Self { + Self { + symbols: interner.symbols, + } + } +} diff --git a/libs/@local/hashql/eval/src/lib.rs b/libs/@local/hashql/eval/src/lib.rs index 16a54ff5638..fbadfc201b7 100644 --- a/libs/@local/hashql/eval/src/lib.rs +++ b/libs/@local/hashql/eval/src/lib.rs @@ -27,6 +27,7 @@ pub mod context; pub mod error; #[cfg(feature = "graph")] pub mod graph; +pub mod intern; pub mod orchestrator; pub mod postgres; diff --git a/libs/@local/hashql/eval/src/orchestrator/codec/decode/mod.rs b/libs/@local/hashql/eval/src/orchestrator/codec/decode/mod.rs index 0c8fd4dcfd0..85883ba9a9b 100644 --- a/libs/@local/hashql/eval/src/orchestrator/codec/decode/mod.rs +++ b/libs/@local/hashql/eval/src/orchestrator/codec/decode/mod.rs @@ -42,7 +42,7 @@ mod tests; /// [`Unknown`]: hashql_core::type::kind::TypeKind::Unknown pub struct Decoder<'env, 'heap, A> { env: &'env Environment<'heap>, - interner: &'env hashql_mir::intern::Interner<'heap>, + interner: &'env crate::intern::Interner<'heap>, alloc: A, } @@ -50,7 +50,7 @@ pub struct Decoder<'env, 'heap, A> { impl<'env, 'heap, A: Allocator> Decoder<'env, 'heap, A> { pub const fn new( env: &'env Environment<'heap>, - interner: &'env hashql_mir::intern::Interner<'heap>, + interner: &'env crate::intern::Interner<'heap>, alloc: A, ) -> Self { Self { diff --git a/libs/@local/hashql/eval/src/orchestrator/codec/decode/tests.rs b/libs/@local/hashql/eval/src/orchestrator/codec/decode/tests.rs index d632d0eec36..64ca25aa1f4 100644 --- a/libs/@local/hashql/eval/src/orchestrator/codec/decode/tests.rs +++ b/libs/@local/hashql/eval/src/orchestrator/codec/decode/tests.rs @@ -6,12 +6,10 @@ use hashql_core::{ symbol::sym, r#type::{TypeId, builder::TypeBuilder, environment::Environment}, }; -use hashql_mir::{ - intern::Interner, - interpret::value::{self, Value}, -}; +use hashql_mir::interpret::value::{self, Value}; use super::{DecodeError, Decoder, JsonValueRef}; +use crate::intern::Interner; fn str_value(content: &str) -> Value<'_, Global> { Value::String(value::Str::from(Rc::::from(content))) @@ -28,7 +26,7 @@ fn decoder<'env, 'heap>( fn primitive_string() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -42,7 +40,7 @@ fn primitive_string() { fn primitive_integer() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -57,7 +55,7 @@ fn primitive_integer() { fn primitive_number() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -72,7 +70,7 @@ fn primitive_number() { fn primitive_boolean_true() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -89,7 +87,7 @@ fn primitive_boolean_true() { fn primitive_boolean_false() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -106,7 +104,7 @@ fn primitive_boolean_false() { fn primitive_null() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -120,7 +118,7 @@ fn primitive_null() { fn primitive_type_mismatch() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -132,7 +130,7 @@ fn primitive_type_mismatch() { fn struct_matching_fields() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -157,7 +155,7 @@ fn struct_matching_fields() { fn struct_missing_field() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -174,7 +172,7 @@ fn struct_missing_field() { fn struct_extra_field() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -192,7 +190,7 @@ fn struct_extra_field() { fn tuple_correct_length() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -221,7 +219,7 @@ fn tuple_correct_length() { fn tuple_length_mismatch() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -236,7 +234,7 @@ fn tuple_length_mismatch() { fn union_first_variant_matches() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -253,7 +251,7 @@ fn union_first_variant_matches() { fn union_second_variant_matches() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -269,7 +267,7 @@ fn union_second_variant_matches() { fn union_no_variant_matches() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -283,7 +281,7 @@ fn union_no_variant_matches() { fn opaque_wraps_inner() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -303,7 +301,7 @@ fn opaque_wraps_inner() { fn list_intrinsic() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -329,7 +327,7 @@ fn list_intrinsic() { fn dict_intrinsic() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -360,7 +358,7 @@ fn dict_intrinsic() { fn intersection_type_error() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -374,7 +372,7 @@ fn intersection_type_error() { fn closure_type_error() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -388,7 +386,7 @@ fn closure_type_error() { fn never_type_error() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -400,7 +398,7 @@ fn never_type_error() { fn unknown_type_integer_fallback() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -415,7 +413,7 @@ fn unknown_type_integer_fallback() { fn unknown_type_float_fallback() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -430,7 +428,7 @@ fn unknown_type_float_fallback() { fn unknown_type_array_becomes_list() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -450,7 +448,7 @@ fn unknown_type_array_becomes_list() { fn unknown_type_non_url_object_becomes_dict() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); @@ -469,7 +467,7 @@ fn unknown_type_non_url_object_becomes_dict() { fn unknown_type_url_object_becomes_struct() { let heap = Heap::new(); let env = Environment::new(&heap); - let interner = Interner::new(&heap); + let interner = Interner::testing(&heap); let types = TypeBuilder::synthetic(&env); let decoder = decoder(&env, &interner); diff --git a/libs/@local/hashql/eval/src/orchestrator/events.rs b/libs/@local/hashql/eval/src/orchestrator/events.rs index e2c6b4cd3b6..dd9e3ce337c 100644 --- a/libs/@local/hashql/eval/src/orchestrator/events.rs +++ b/libs/@local/hashql/eval/src/orchestrator/events.rs @@ -146,6 +146,7 @@ impl AppendEventLog { } impl Default for AppendEventLog { + #[inline] fn default() -> Self { Self::new() } diff --git a/libs/@local/hashql/eval/src/orchestrator/mod.rs b/libs/@local/hashql/eval/src/orchestrator/mod.rs index 56e7acb85e2..7cb4198ecaa 100644 --- a/libs/@local/hashql/eval/src/orchestrator/mod.rs +++ b/libs/@local/hashql/eval/src/orchestrator/mod.rs @@ -59,7 +59,7 @@ pub use self::{ error::{OrchestratorDiagnostic, OrchestratorDiagnosticCategory}, events::{AppendEventLog, Event, EventLog}, }; -use crate::{context::EvalContext, postgres::PreparedQueries}; +use crate::{context::CodeExecutionContext, postgres::PreparedQueries}; pub mod codec; pub mod error; @@ -119,7 +119,7 @@ impl Deref for Indexed { pub struct Orchestrator<'env, 'ctx, 'heap, C, E, A: Allocator> { client: C, queries: &'env PreparedQueries<'heap, A>, - context: &'env EvalContext<'ctx, 'heap, A>, + context: &'env CodeExecutionContext<'ctx, 'heap, A>, /// Event sink for execution tracing. See [`EventLog`]. pub event_log: E, } @@ -128,7 +128,7 @@ impl<'env, 'ctx, 'heap, C, A: Allocator> Orchestrator<'env, 'ctx, 'heap, C, (), pub const fn new( client: C, queries: &'env PreparedQueries<'heap, A>, - context: &'env EvalContext<'ctx, 'heap, A>, + context: &'env CodeExecutionContext<'ctx, 'heap, A>, ) -> Self { Self { client, diff --git a/libs/@local/hashql/eval/src/orchestrator/partial.rs b/libs/@local/hashql/eval/src/orchestrator/partial.rs index 385c86aa51b..0231885b92c 100644 --- a/libs/@local/hashql/eval/src/orchestrator/partial.rs +++ b/libs/@local/hashql/eval/src/orchestrator/partial.rs @@ -31,7 +31,6 @@ use hashql_core::{ r#type::{TypeId, environment::Environment}, }; use hashql_mir::{ - intern::Interner, interpret::value::{Int, Num, Opaque, StructBuilder, Value}, pass::execution::{ VertexType, @@ -46,7 +45,7 @@ use super::{ codec::{JsonValueRef, decode::Decoder}, error::BridgeError, }; -use crate::postgres::ColumnDescriptor; +use crate::{intern::Interner, postgres::ColumnDescriptor}; macro_rules! hydrate { ($this:ident -> $entry:ident $(-> $field:ident)+ = $value:expr) => { diff --git a/libs/@local/hashql/eval/src/postgres/filter/mod.rs b/libs/@local/hashql/eval/src/postgres/filter/mod.rs index 588d832dd71..b020e109294 100644 --- a/libs/@local/hashql/eval/src/postgres/filter/mod.rs +++ b/libs/@local/hashql/eval/src/postgres/filter/mod.rs @@ -62,7 +62,7 @@ use super::{ traverse::eval_entity_path, types::{IntegerType, integer_type}, }; -use crate::{context::EvalContext, error::EvalDiagnosticIssues}; +use crate::{context::CodeGenerationContext, error::EvalDiagnosticIssues}; /// Internal representation of a continuation result before casting to the SQL composite type. /// @@ -233,7 +233,7 @@ fn finish_switch_int( /// internal buffer retrievable via [`Self::into_diagnostics`]. pub(crate) struct GraphReadFilterCompiler<'ctx, 'heap, A: Allocator = Global, S: Allocator = Global> { - context: &'ctx EvalContext<'ctx, 'heap, A>, + context: &'ctx CodeGenerationContext<'ctx, 'heap, A>, body: &'ctx Body<'heap>, env: Local, @@ -247,7 +247,7 @@ pub(crate) struct GraphReadFilterCompiler<'ctx, 'heap, A: Allocator = Global, S: impl<'ctx, 'heap, A: Allocator, S: Allocator> GraphReadFilterCompiler<'ctx, 'heap, A, S> { pub(crate) fn new( - context: &'ctx EvalContext<'ctx, 'heap, A>, + context: &'ctx CodeGenerationContext<'ctx, 'heap, A>, body: &'ctx Body<'heap>, env: Local, scratch: S, diff --git a/libs/@local/hashql/eval/src/postgres/filter/tests.rs b/libs/@local/hashql/eval/src/postgres/filter/tests.rs index cc7ef967ac3..486ec2b8707 100644 --- a/libs/@local/hashql/eval/src/postgres/filter/tests.rs +++ b/libs/@local/hashql/eval/src/postgres/filter/tests.rs @@ -40,7 +40,7 @@ use sqruff_lib::core::{config::FluffConfig, linter::core::Linter}; use sqruff_lib_core::dialects::init::DialectKind; use crate::{ - context::EvalContext, + context::CodeGenerationContext, postgres::{DatabaseContext, PostgresCompiler, filter::GraphReadFilterCompiler}, }; @@ -48,6 +48,7 @@ use crate::{ /// and returns everything needed for compilation. struct Fixture<'heap> { env: Environment<'heap>, + interner: crate::intern::Interner<'heap>, bodies: DefIdVec, &'heap Heap>, execution: DefIdVec>, &'heap Heap>, } @@ -89,6 +90,7 @@ impl<'heap> Fixture<'heap> { Self { env, + interner: interner.into(), bodies, execution, } @@ -151,11 +153,10 @@ fn format_body<'heap>(fixture: &Fixture<'heap>, heap: &'heap Heap) -> String { fn compile_filter_islands<'heap>(fixture: &Fixture<'heap>, heap: &'heap Heap) -> FilterReport { let mut scratch = Scratch::new(); let def = fixture.def(); - let interner = Interner::new(heap); - let context = EvalContext::new_in( + let context = CodeGenerationContext::new_in( &fixture.env, - &interner, + &fixture.interner, &fixture.bodies, &fixture.execution, heap, @@ -278,11 +279,10 @@ fn compile_full_query_with_mask<'heap>( ) -> QueryReport { let mut scratch = Scratch::new(); let def = fixture.def(); - let interner = Interner::new(heap); - let mut context = EvalContext::new_in( + let mut context = CodeGenerationContext::new_in( &fixture.env, - &interner, + &fixture.interner, &fixture.bodies, &fixture.execution, heap, diff --git a/libs/@local/hashql/eval/src/postgres/mod.rs b/libs/@local/hashql/eval/src/postgres/mod.rs index 43744c10948..3dc70a6b247 100644 --- a/libs/@local/hashql/eval/src/postgres/mod.rs +++ b/libs/@local/hashql/eval/src/postgres/mod.rs @@ -64,7 +64,7 @@ pub use self::{ continuation::ContinuationField, parameters::{Parameter, ParameterIndex, ParameterValue, Parameters, TemporalAxis}, }; -use crate::context::EvalContext; +use crate::context::CodeGenerationContext; mod continuation; pub(crate) mod error; @@ -235,12 +235,12 @@ impl<'heap, A: Allocator> PreparedQueries<'heap, A> { /// Compiles Postgres-targeted MIR islands into a single PostgreSQL `SELECT`. /// /// Created per evaluation and used to compile [`GraphRead`] terminators. Compilation emits -/// diagnostics into the shared [`EvalContext`] rather than returning `Result`, so multiple -/// errors can be reported from a single compilation pass. +/// diagnostics into the shared [`CodeGenerationContext`] rather than returning `Result`, so +/// multiple errors can be reported from a single compilation pass. /// /// [`GraphRead`]: hashql_mir::body::terminator::GraphRead pub struct PostgresCompiler<'eval, 'ctx, 'heap, A: Allocator, S: Allocator> { - context: &'eval mut EvalContext<'ctx, 'heap, A>, + context: &'eval mut CodeGenerationContext<'ctx, 'heap, A>, alloc: A, scratch: S, @@ -257,7 +257,7 @@ pub struct PostgresCompiler<'eval, 'ctx, 'heap, A: Allocator, S: Allocator> { impl<'eval, 'ctx, 'heap, A: Allocator, S: BumpAllocator> PostgresCompiler<'eval, 'ctx, 'heap, A, S> { - pub fn new_in(context: &'eval mut EvalContext<'ctx, 'heap, A>, scratch: S) -> Self + pub fn new_in(context: &'eval mut CodeGenerationContext<'ctx, 'heap, A>, scratch: S) -> Self where A: Clone, { diff --git a/libs/@local/hashql/eval/src/postgres/parameters.rs b/libs/@local/hashql/eval/src/postgres/parameters.rs index 411a93544d9..e929f18fb9e 100644 --- a/libs/@local/hashql/eval/src/postgres/parameters.rs +++ b/libs/@local/hashql/eval/src/postgres/parameters.rs @@ -36,6 +36,7 @@ impl Display for ParameterIndex { } impl From for Expression { + #[inline] fn from(value: ParameterIndex) -> Self { Self::Parameter(value.as_usize() + 1) } diff --git a/libs/@local/hashql/eval/src/postgres/projections.rs b/libs/@local/hashql/eval/src/postgres/projections.rs index 59dbdafd577..f40f6752dcc 100644 --- a/libs/@local/hashql/eval/src/postgres/projections.rs +++ b/libs/@local/hashql/eval/src/postgres/projections.rs @@ -20,6 +20,7 @@ enum ComputedColumn { } impl From for ColumnName<'_> { + #[inline] fn from(value: ComputedColumn) -> Self { match value { ComputedColumn::EntityTypeIds => ColumnName::from(Identifier::from("entity_type_ids")), diff --git a/libs/@local/hashql/eval/tests/orchestrator/execution.rs b/libs/@local/hashql/eval/tests/orchestrator/execution.rs index 09f3dcd74cf..5bad3bba6ab 100644 --- a/libs/@local/hashql/eval/tests/orchestrator/execution.rs +++ b/libs/@local/hashql/eval/tests/orchestrator/execution.rs @@ -1,18 +1,19 @@ -use alloc::alloc::Global; use core::mem; use hashql_compiletest::pipeline::Pipeline; -use hashql_core::{heap::ResetAllocator as _, span::SpanId}; +use hashql_core::{ + heap::{Heap, ResetAllocator as _}, + span::SpanId, +}; use hashql_diagnostics::{Diagnostic, diagnostic::BoxedDiagnostic}; use hashql_eval::{ - context::EvalContext, + context::{CodeExecutionContext, CodeGenerationContext}, orchestrator::{AppendEventLog, Event, Orchestrator}, postgres::PostgresCompiler, }; use hashql_mir::{ body::Body, def::{DefId, DefIdSlice, DefIdVec}, - intern::Interner, interpret::{Inputs, value::Value}, }; use tokio::runtime; @@ -23,7 +24,7 @@ use tokio_postgres::Client; /// Holds the MIR artifacts needed to build typed inputs (via the decoder /// and the environment) before proceeding to execution. pub(crate) struct Lowered<'heap> { - pub interner: Interner<'heap>, + pub interner: hashql_mir::intern::Interner<'heap>, pub entry: DefId, pub bodies: DefIdVec>, } @@ -65,16 +66,16 @@ pub(crate) fn run<'heap>( runtime: &runtime::Runtime, client: &Client, - inputs: &Inputs<'heap, Global>, + inputs: &Inputs<'heap, &'heap Heap>, - lowered: &mut Lowered<'heap>, -) -> Result<(Value<'heap, Global>, Vec), BoxedDiagnostic<'static, SpanId>> { + mut lowered: Lowered<'heap>, +) -> Result<(Value<'heap, &'heap Heap>, Vec), BoxedDiagnostic<'static, SpanId>> { run_impl( pipeline, runtime, client, inputs, - &lowered.interner, + lowered.interner, lowered.entry, &mut lowered.bodies, ) @@ -94,12 +95,12 @@ pub(crate) fn execute<'heap>( runtime: &runtime::Runtime, client: &Client, - inputs: &Inputs<'heap, Global>, + inputs: &Inputs<'heap, &'heap Heap>, - interner: &Interner<'heap>, + interner: hashql_mir::intern::Interner<'heap>, entry: DefId, bodies: &mut DefIdSlice>, -) -> Result<(Value<'heap, Global>, Vec), BoxedDiagnostic<'static, SpanId>> { +) -> Result<(Value<'heap, &'heap Heap>, Vec), BoxedDiagnostic<'static, SpanId>> { run_impl(pipeline, runtime, client, inputs, interner, entry, bodies) } @@ -116,18 +117,19 @@ fn run_impl<'heap>( runtime: &runtime::Runtime, client: &Client, - inputs: &Inputs<'heap, Global>, + inputs: &Inputs<'heap, &'heap Heap>, - interner: &Interner<'heap>, + interner: hashql_mir::intern::Interner<'heap>, entry: DefId, bodies: &mut DefIdSlice>, -) -> Result<(Value<'heap, Global>, Vec), BoxedDiagnostic<'static, SpanId>> { - pipeline.transform(interner, bodies)?; - let analysis = pipeline.prepare(interner, bodies)?; +) -> Result<(Value<'heap, &'heap Heap>, Vec), BoxedDiagnostic<'static, SpanId>> { + pipeline.transform(&interner, bodies)?; + let analysis = pipeline.prepare(&interner, bodies)?; - let mut context = EvalContext::new_in( + let interner = interner.into(); + let mut context = CodeGenerationContext::new_in( &pipeline.env, - interner, + &interner, bodies, &analysis, pipeline.heap, @@ -141,11 +143,12 @@ fn run_impl<'heap>( pipeline.diagnostics.append(&mut diagnostics.boxed()); let event_log = AppendEventLog::new(); + let context = CodeExecutionContext::from(context); let orchestrator = Orchestrator::new(PostgresClient(client), &queries, &context).with_event_log(&event_log); let value = runtime - .block_on(orchestrator.run(inputs, entry, [])) + .block_on(orchestrator.run_in(inputs, entry, [], pipeline.heap)) .map_err(Diagnostic::generalize) .map_err(Diagnostic::boxed)?; diff --git a/libs/@local/hashql/eval/tests/orchestrator/inputs.rs b/libs/@local/hashql/eval/tests/orchestrator/inputs.rs index d2276c909c8..7fe8d0d7160 100644 --- a/libs/@local/hashql/eval/tests/orchestrator/inputs.rs +++ b/libs/@local/hashql/eval/tests/orchestrator/inputs.rs @@ -1,23 +1,19 @@ -use alloc::alloc::Global; +use alloc::rc::Rc; -use hashql_compiletest::pipeline::Pipeline; use hashql_core::{ - heap::Heap, - module::std_lib::graph::types::{ - knowledge::entity, principal::actor_group::web::types as web_types, - }, - symbol::sym, - r#type::TypeBuilder, + heap::{FromIn as _, Heap}, + intern::InternSet, + symbol::{Symbol, sym}, }; -use hashql_eval::orchestrator::codec::{Decoder, JsonValueRef}; -use hashql_mir::{ - intern::Interner, - interpret::{ - Inputs, - value::{self, Value}, - }, +use hashql_mir::interpret::{ + Inputs, + value::{self, StructBuilder, Value}, }; -use type_system::knowledge::entity::id::EntityUuid; +use type_system::{ + knowledge::entity::id::EntityUuid, + principal::actor_group::{ActorGroupEntityUuid, WebId}, +}; +use uuid::Uuid; use crate::{ directives::{AxisBound, AxisDirectives, AxisInterval}, @@ -25,70 +21,75 @@ use crate::{ }; /// Constructs `Opaque(Timestamp, Integer(ms))`. -fn timestamp_value(ms: i128) -> Value<'static, Global> { +fn timestamp_value(heap: &Heap, ms: i128) -> Value<'_, &Heap> { Value::Opaque(value::Opaque::new( sym::path::Timestamp, - Value::Integer(value::Int::from(ms)), + Rc::new_in(Value::Integer(value::Int::from(ms)), heap), )) } /// Constructs `Opaque(UnboundedTemporalBound, Unit)`. -fn unbounded_bound() -> Value<'static, Global> { +fn unbounded_bound(heap: &Heap) -> Value<'_, &Heap> { Value::Opaque(value::Opaque::new( sym::path::UnboundedTemporalBound, - Value::Unit, + Rc::new_in(Value::Unit, heap), )) } /// Constructs `Opaque(ExclusiveTemporalBound, Timestamp(ms))`. -fn exclusive_bound(ms: i128) -> Value<'static, Global> { +fn exclusive_bound(heap: &Heap, ms: i128) -> Value<'_, &Heap> { Value::Opaque(value::Opaque::new( sym::path::ExclusiveTemporalBound, - timestamp_value(ms), + Rc::new_in(timestamp_value(heap, ms), heap), )) } /// Constructs `Opaque(Interval, {end: .., start: ..})`. /// -/// Fields are sorted lexicographically (`end` before `start`). +/// Field order in `push` calls does not matter; [`StructBuilder::finish`] +/// sorts fields lexicographically. fn interval_value<'heap>( - interner: &Interner<'heap>, - start: Value<'heap, Global>, - end: Value<'heap, Global>, -) -> Value<'heap, Global> { - // Fields sorted: "end" < "start" - let fields = interner.symbols.intern_slice(&[sym::end, sym::start]); - let values = vec![end, start]; + heap: &'heap Heap, + symbols: &InternSet<'heap, [Symbol<'heap>]>, + start: Value<'heap, &'heap Heap>, + end: Value<'heap, &'heap Heap>, +) -> Value<'heap, &'heap Heap> { + let mut builder = StructBuilder::<_, 2>::new(); + builder.push(sym::end, end); + builder.push(sym::start, start); + + let inner = builder.finish(symbols, heap); Value::Opaque(value::Opaque::new( sym::path::Interval, - Value::Struct(value::Struct::new(fields, values).expect("interval struct is valid")), + Rc::new_in(Value::Struct(inner), heap), )) } /// Converts an [`AxisInterval`] to a `Value` representing a temporal /// interval: `Opaque(Interval, {start: , end: })`. fn axis_interval_to_value<'heap>( - interner: &Interner<'heap>, + heap: &'heap Heap, + symbols: &InternSet<'heap, [Symbol<'heap>]>, interval: &AxisInterval, -) -> Value<'heap, Global> { +) -> Value<'heap, &'heap Heap> { let start = match interval.start { - AxisBound::Unbounded => unbounded_bound(), + AxisBound::Unbounded => unbounded_bound(heap), AxisBound::Included(ms) => Value::Opaque(value::Opaque::new( sym::path::InclusiveTemporalBound, - timestamp_value(ms), + Rc::new_in(timestamp_value(heap, ms), heap), )), - AxisBound::Excluded(ms) => exclusive_bound(ms), + AxisBound::Excluded(ms) => exclusive_bound(heap, ms), }; let end = match interval.end { - AxisBound::Unbounded => unbounded_bound(), + AxisBound::Unbounded => unbounded_bound(heap), AxisBound::Included(ms) => Value::Opaque(value::Opaque::new( sym::path::InclusiveTemporalBound, - timestamp_value(ms), + Rc::new_in(timestamp_value(heap, ms), heap), )), - AxisBound::Excluded(ms) => exclusive_bound(ms), + AxisBound::Excluded(ms) => exclusive_bound(heap, ms), }; - interval_value(interner, start, end) + interval_value(heap, symbols, start, end) } /// Returns `true` if the interval is a point (both bounds are Included with @@ -108,9 +109,10 @@ fn is_point(interval: &AxisInterval) -> Option { /// determines which axis is pinned (a point `(T)`) and which is variable /// (a range `[a, b)` or defaulting to unbounded). fn temporal_axes_from_directives<'heap>( - interner: &Interner<'heap>, + heap: &'heap Heap, + symbols: &InternSet<'heap, [Symbol<'heap>]>, directives: &AxisDirectives, -) -> Value<'heap, Global> { +) -> Value<'heap, &'heap Heap> { let far_future_ms: i128 = 4_102_444_800_000; // 2100-01-01T00:00:00Z let default_variable = || AxisInterval { start: AxisBound::Unbounded, @@ -167,15 +169,23 @@ fn temporal_axes_from_directives<'heap>( } }; - let pinned = Value::Opaque(value::Opaque::new(pinned_axis, timestamp_value(pinned_ms))); + let pinned = Value::Opaque(value::Opaque::new( + pinned_axis, + Rc::new_in(timestamp_value(heap, pinned_ms), heap), + )); let variable = Value::Opaque(value::Opaque::new( variable_axis_name, - axis_interval_to_value(interner, &variable_interval), + Rc::new_in( + axis_interval_to_value(heap, symbols, &variable_interval), + heap, + ), )); - // "pinned" < "variable" lexicographically. - let fields = interner.symbols.intern_slice(&[sym::pinned, sym::variable]); - let values = vec![pinned, variable]; + let mut builder = value::StructBuilder::<_, 2>::new(); + builder.push(sym::pinned, pinned); + builder.push(sym::variable, variable); + + let inner = builder.finish(symbols, heap); let wrapper_name = if pinned_axis == sym::path::TransactionTime { sym::path::PinnedTransactionTimeTemporalAxes @@ -185,97 +195,145 @@ fn temporal_axes_from_directives<'heap>( Value::Opaque(value::Opaque::new( wrapper_name, - Value::Struct(value::Struct::new(fields, values).expect("axes struct is valid")), + Rc::new_in(Value::Struct(inner), heap), )) } +fn option<'heap, T>( + heap: &'heap Heap, + value: Option, + on_value: impl FnOnce(&'heap Heap, T) -> value::Value<'heap, &'heap Heap>, +) -> value::Value<'heap, &'heap Heap> { + value.map_or_else( + || { + value::Value::Opaque(value::Opaque::new( + sym::path::None, + Rc::new_in(value::Value::Unit, heap), + )) + }, + |value| { + value::Value::Opaque(value::Opaque::new( + sym::path::Some, + Rc::new_in(on_value(heap, value), heap), + )) + }, + ) +} + /// Builds the shared input set from seeded entity data and axis directives. /// -/// Uses the decoder and the post-lowering type environment to construct -/// properly typed `Value`s for entity UUIDs and entity IDs. The input names -/// match what J-Expr test files reference via `["input", "", ""]`. +/// Constructs interpreter [`Value`]s directly from the Rust-typed seed data, +/// mirroring the opaque wrapping structure of the HashQL type system +/// (e.g. `EntityId(Struct { web_id: WebId(ActorGroupEntityUuid(Uuid(String))), ... })`). +/// +/// The input names match what J-Expr test files reference via +/// `["input", "", ""]`. pub(crate) fn build_inputs<'heap>( heap: &'heap Heap, - pipeline: &Pipeline<'heap>, - interner: &Interner<'heap>, + symbols: &InternSet<'heap, [Symbol<'heap>]>, entities: &SeededEntities, directives: &AxisDirectives, -) -> Inputs<'heap, Global> { - let mut inputs = Inputs::new(); - let decoder = Decoder::new(&pipeline.env, interner, Global); - let ty = TypeBuilder::synthetic(&pipeline.env); - let entity_uuid_type = entity::types::entity_uuid(&ty, None); - let entity_id_type = entity::types::entity_id(&ty, None); +) -> Inputs<'heap, &'heap Heap> { + let mut inputs = Inputs::new_in(heap); - // Insert an EntityUuid-typed input. - let insert_uuid = |inputs: &mut Inputs<'heap, Global>, name: &str, uuid: &EntityUuid| { - let uuid_str = uuid.to_string(); - let value = decoder - .decode(entity_uuid_type, JsonValueRef::String(&uuid_str)) - .expect("could not decode EntityUuid input"); + let string = |value: &str| value::Value::String(value::Str::from(Rc::from_in(value, heap))); + + let uuid = |value: Uuid| { + value::Value::Opaque(value::Opaque::new( + sym::path::Uuid, + Rc::new_in(string(value.to_string().as_str()), heap), + )) + }; + + let entity_uuid = |value: EntityUuid| { + value::Value::Opaque(value::Opaque::new( + sym::path::EntityUuid, + Rc::new_in(uuid(value.into()), heap), + )) + }; + + let actor_group_entity_uuid = |value: ActorGroupEntityUuid| { + value::Value::Opaque(value::Opaque::new( + sym::path::ActorGroupEntityUuid, + Rc::new_in(uuid(value.into()), heap), + )) + }; + + let web_id = |value: WebId| { + value::Value::Opaque(value::Opaque::new( + sym::path::WebId, + Rc::new_in(actor_group_entity_uuid(value.into()), heap), + )) + }; - inputs.insert(heap.intern_symbol(name), value); + let draft_id = |value: Option| { + option(heap, value, |heap, value| { + value::Value::Opaque(value::Opaque::new( + sym::path::DraftId, + Rc::new_in(uuid(value.into()), heap), + )) + }) }; + let entity_id = |value: type_system::knowledge::entity::id::EntityId| { + let mut builder = StructBuilder::<_, 3>::new(); + builder.push(sym::web_id, web_id(value.web_id)); + builder.push(sym::entity_uuid, entity_uuid(value.entity_uuid)); + builder.push(sym::draft_id, draft_id(value.draft_id)); + + let r#struct = builder.finish(symbols, heap); + let inner = value::Value::Struct(r#struct); + value::Value::Opaque(value::Opaque::new( + sym::path::EntityId, + Rc::new_in(inner, heap), + )) + }; + + // Insert an EntityUuid-typed input. + let insert_entity_uuid = + |inputs: &mut Inputs<'heap, &'heap Heap>, name: &str, uuid: EntityUuid| { + inputs.insert(heap.intern_symbol(name), entity_uuid(uuid)); + }; + // Insert a full EntityId-typed input. let insert_entity_id = - |inputs: &mut Inputs<'heap, Global>, + |inputs: &mut Inputs<'heap, &'heap Heap>, name: &str, - id: &type_system::knowledge::entity::EntityId| { - let json = serde_json::json!({ - "web_id": id.web_id.to_string(), - "entity_uuid": id.entity_uuid.to_string(), - "draft_id": id.draft_id.map(|draft| draft.to_string()), - }); - let value = decoder - .decode(entity_id_type, JsonValueRef::from(&json)) - .expect("could not decode EntityId input"); - - inputs.insert(heap.intern_symbol(name), value); + id: type_system::knowledge::entity::EntityId| { + inputs.insert(heap.intern_symbol(name), entity_id(id)); }; - insert_uuid(&mut inputs, "alice_uuid", &entities.alice.entity_uuid); - insert_uuid(&mut inputs, "bob_uuid", &entities.bob.entity_uuid); - insert_uuid(&mut inputs, "org_uuid", &entities.organization.entity_uuid); - insert_uuid( + insert_entity_uuid(&mut inputs, "alice_uuid", entities.alice.entity_uuid); + insert_entity_uuid(&mut inputs, "bob_uuid", entities.bob.entity_uuid); + insert_entity_uuid(&mut inputs, "org_uuid", entities.organization.entity_uuid); + insert_entity_uuid( &mut inputs, "friend_link_uuid", - &entities.friend_link.entity_uuid, + entities.friend_link.entity_uuid, ); - insert_uuid( + insert_entity_uuid( &mut inputs, "draft_alice_uuid", - &entities.draft_alice.entity_uuid, + entities.draft_alice.entity_uuid, ); - insert_entity_id(&mut inputs, "alice_id", &entities.alice); - insert_entity_id(&mut inputs, "bob_id", &entities.bob); - insert_entity_id(&mut inputs, "org_id", &entities.organization); - insert_entity_id(&mut inputs, "friend_link_id", &entities.friend_link); - insert_entity_id(&mut inputs, "draft_alice_id", &entities.draft_alice); + insert_entity_id(&mut inputs, "alice_id", entities.alice); + insert_entity_id(&mut inputs, "bob_id", entities.bob); + insert_entity_id(&mut inputs, "org_id", entities.organization); + insert_entity_id(&mut inputs, "friend_link_id", entities.friend_link); + insert_entity_id(&mut inputs, "draft_alice_id", entities.draft_alice); // WebId input (all seeded entities share the same web). - let web_id_type = web_types::web_id(&ty, None); - let web_id_value = decoder - .decode( - web_id_type, - JsonValueRef::String(&entities.alice.web_id.to_string()), - ) - .expect("could not decode WebId input"); - inputs.insert(heap.intern_symbol("web_id"), web_id_value); + inputs.insert(heap.intern_symbol("web_id"), web_id(entities.alice.web_id)); // String inputs for property-based filtering. - let string_type = ty.string(); - let alice_name = decoder - .decode(string_type, JsonValueRef::String("Alice")) - .expect("could not decode string input"); - inputs.insert(heap.intern_symbol("alice_name"), alice_name); + inputs.insert(heap.intern_symbol("alice_name"), string("Alice")); // Temporal axes from directives (or default: unbounded decision time, // far-future transaction pin). inputs.insert( heap.intern_symbol("temporal_axes"), - temporal_axes_from_directives(interner, directives), + temporal_axes_from_directives(heap, symbols, directives), ); inputs diff --git a/libs/@local/hashql/eval/tests/orchestrator/main.rs b/libs/@local/hashql/eval/tests/orchestrator/main.rs index 53d4b4ea7be..be5f790b810 100644 --- a/libs/@local/hashql/eval/tests/orchestrator/main.rs +++ b/libs/@local/hashql/eval/tests/orchestrator/main.rs @@ -109,7 +109,7 @@ fn run_jexpr_test( let mut pipeline = Pipeline::new(&heap); // Lower first so the type environment is populated, then build inputs. - let mut lowered = match execution::lower(&mut pipeline, &bytes) { + let lowered = match execution::lower(&mut pipeline, &bytes) { Ok(lowered) => lowered, Err(diagnostic) => { let rendered = render_failure(&source, &pipeline, &diagnostic); @@ -119,8 +119,7 @@ fn run_jexpr_test( let inputs = build_inputs( &heap, - &pipeline, - &lowered.interner, + &lowered.interner.symbols, &context.entities, &axis_directives, ); @@ -130,7 +129,7 @@ fn run_jexpr_test( runtime, context.store.as_client(), &inputs, - &mut lowered, + lowered, ) { Ok((value, events)) => { let rendered = render_success(&source, &value, &events, &pipeline)?; @@ -162,8 +161,7 @@ fn run_programmatic_test( let inputs = build_inputs( &heap, - &pipeline, - &interner, + &interner.symbols, &context.entities, &AxisDirectives::default(), ); @@ -177,7 +175,7 @@ fn run_programmatic_test( runtime, context.store.as_client(), &inputs, - &interner, + interner, entry, &mut bodies, ) { diff --git a/libs/@local/hashql/eval/tests/orchestrator/output.rs b/libs/@local/hashql/eval/tests/orchestrator/output.rs index e146bf6e13d..5287ad15a3f 100644 --- a/libs/@local/hashql/eval/tests/orchestrator/output.rs +++ b/libs/@local/hashql/eval/tests/orchestrator/output.rs @@ -1,4 +1,4 @@ -use alloc::alloc::Global; +use core::alloc::Allocator; use std::{collections::HashMap, fs, path::Path, sync::LazyLock}; use error_stack::{Report, ResultExt as _}; @@ -108,9 +108,9 @@ fn normalize(input: &str) -> String { /// /// Returns [`TestError::Serialization`] if the value cannot be serialized to /// JSON. -pub(crate) fn render_success( +pub(crate) fn render_success( source: &str, - value: &Value<'_, Global>, + value: &Value<'_, A>, events: &[Event], pipeline: &Pipeline<'_>, ) -> Result> {