From 95715c49bf9a9baab7f4c35fd22d0a1aead448eb Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:33:47 +0200 Subject: [PATCH] feat: implement Null -> Unit conversion --- libs/@local/hashql/mir/src/reify/atom.rs | 3 +- libs/@local/hashql/mir/src/reify/rvalue.rs | 5 ++ .../ui/pass/inline/filter-aggressive.stdout | 6 +- .../mir/tests/ui/reify/empty-tuple.jsonc | 3 + .../mir/tests/ui/reify/empty-tuple.stdout | 9 +++ .../tests/ui/reify/null-value-in-binary.jsonc | 12 ++++ .../ui/reify/null-value-in-binary.stdout | 55 +++++++++++++++++++ .../mir/tests/ui/reify/null-value.jsonc | 3 + .../mir/tests/ui/reify/null-value.stdout | 5 ++ 9 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 libs/@local/hashql/mir/tests/ui/reify/empty-tuple.jsonc create mode 100644 libs/@local/hashql/mir/tests/ui/reify/empty-tuple.stdout create mode 100644 libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.jsonc create mode 100644 libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.stdout create mode 100644 libs/@local/hashql/mir/tests/ui/reify/null-value.jsonc create mode 100644 libs/@local/hashql/mir/tests/ui/reify/null-value.stdout diff --git a/libs/@local/hashql/mir/src/reify/atom.rs b/libs/@local/hashql/mir/src/reify/atom.rs index ca26410b975..5779e1d4dcb 100644 --- a/libs/@local/hashql/mir/src/reify/atom.rs +++ b/libs/@local/hashql/mir/src/reify/atom.rs @@ -1,6 +1,6 @@ use core::alloc::Allocator; -use hashql_core::{id::Id as _, r#type::kind::TypeKind}; +use hashql_core::{id::Id as _, r#type::kind::TypeKind, value::Primitive}; use hashql_hir::node::{ Node, access::{Access, FieldAccess, IndexAccess}, @@ -165,6 +165,7 @@ impl<'heap, A: Allocator, S: Allocator> Reifier<'_, '_, '_, '_, 'heap, A, S> { // In the future this would be a simple FnPtr Operand::Constant(Constant::Unit) } + NodeKind::Data(Data::Primitive(Primitive::Null)) => Operand::Constant(Constant::Unit), NodeKind::Data(Data::Primitive(primitive)) => { // First try if we can promote the primitive to a non-opaque constant: let constant = match Int::try_from(primitive) { diff --git a/libs/@local/hashql/mir/src/reify/rvalue.rs b/libs/@local/hashql/mir/src/reify/rvalue.rs index 4cb798feab5..f5c82901a18 100644 --- a/libs/@local/hashql/mir/src/reify/rvalue.rs +++ b/libs/@local/hashql/mir/src/reify/rvalue.rs @@ -4,6 +4,7 @@ use hashql_core::{ id::{Id as _, IdVec}, symbol::sym, r#type::{TypeBuilder, Typed, builder}, + value::Primitive, }; use hashql_hir::node::{ HirPtr, Node, @@ -37,6 +38,7 @@ use crate::{ impl<'mir, 'heap, A: Allocator, S: Allocator> Reifier<'_, 'mir, '_, '_, 'heap, A, S> { fn rvalue_data(&mut self, data: Data<'heap>) -> RValue<'heap> { match data { + Data::Primitive(Primitive::Null) => RValue::Load(Operand::Constant(Constant::Unit)), Data::Primitive(primitive) => { // First try if we can promote the primitive to a non-opaque constant: let constant = match Int::try_from(primitive) { @@ -76,6 +78,9 @@ impl<'mir, 'heap, A: Allocator, S: Allocator> Reifier<'_, 'mir, '_, '_, 'heap, A operands, }) } + Data::Tuple(Tuple { fields }) if fields.is_empty() => { + RValue::Load(Operand::Constant(Constant::Unit)) + } Data::Tuple(Tuple { fields }) => { let mut operands = IdVec::with_capacity_in(fields.len(), self.context.mir.heap); for &field in fields { 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 b0abcece659..322037912fe 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 @@ -14,7 +14,7 @@ fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity let %2: Boolean bb0(): { - %2 = %1.link_data == null + %2 = %1.link_data == () return %2 } @@ -55,7 +55,7 @@ fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity let %2: Boolean bb0(): { - %2 = %1.link_data == null + %2 = %1.link_data == () return %2 } @@ -96,7 +96,7 @@ fn {graph::read::filter@7}(%0: (), %1: ::graph::types::knowledge::entity::Entity let %2: Boolean bb0(): { - %2 = %1.link_data == null + %2 = %1.link_data == () return %2 } diff --git a/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.jsonc b/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.jsonc new file mode 100644 index 00000000000..757580b1067 --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.jsonc @@ -0,0 +1,3 @@ +//@ run: pass +//@ description: empty tuple should be reified as a unit constant +{ "#tuple": [] } diff --git a/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.stdout b/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.stdout new file mode 100644 index 00000000000..07a2f6ab8eb --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/empty-tuple.stdout @@ -0,0 +1,9 @@ +*thunk {thunk#0}() -> () { + let %0: () + + bb0(): { + %0 = () + + return %0 + } +} \ No newline at end of file diff --git a/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.jsonc b/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.jsonc new file mode 100644 index 00000000000..cd52f7fab5a --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.jsonc @@ -0,0 +1,12 @@ +//@ run: pass +//@ description: null should be promoted to a unit constant +[ + "if", + { "#literal": true }, + [ + "let", + "foo", + { "#literal": null }, + ["let", "bar", { "#literal": null }, ["==", "foo", "bar"]] + ] +] diff --git a/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.stdout b/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.stdout new file mode 100644 index 00000000000..111a2321f63 --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/null-value-in-binary.stdout @@ -0,0 +1,55 @@ +fn {ctor#::core::option::Some}(%0: (), %1: Boolean) -> ::core::option::Some { + let %2: ::core::option::Some + + bb0(): { + %2 = opaque(::core::option::Some, %1) + + return %2 + } +} + +fn {ctor#::core::option::None}(%0: ()) -> ::core::option::None { + let %1: ::core::option::None + + bb0(): { + %1 = opaque(::core::option::None, ()) + + return %1 + } +} + +*thunk {thunk#7}() -> ::core::option::None | ::core::option::Some { + let %0: ::core::option::None | ::core::option::Some + let %1: Null + let %2: Null + let %3: Boolean + let %4: (Boolean) -> ::core::option::Some + let %5: ::core::option::Some + let %6: () -> ::core::option::None + let %7: ::core::option::None + + bb0(): { + switchInt(true) -> [0: bb2(), 1: bb1()] + } + + bb1(): { + %1 = () + %2 = () + %3 = %1 == %2 + %4 = closure(({ctor#::core::option::Some} as FnPtr), ()) + %5 = apply %4.0 %4.1 %3 + + goto -> bb3(%5) + } + + bb2(): { + %6 = closure(({ctor#::core::option::None} as FnPtr), ()) + %7 = apply %6.0 %6.1 + + goto -> bb3(%7) + } + + bb3(%0): { + return %0 + } +} \ No newline at end of file diff --git a/libs/@local/hashql/mir/tests/ui/reify/null-value.jsonc b/libs/@local/hashql/mir/tests/ui/reify/null-value.jsonc new file mode 100644 index 00000000000..961b2bfe252 --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/null-value.jsonc @@ -0,0 +1,3 @@ +//@ run: pass +//@ description: null should be promoted to a unit constant +["let", "foo", { "#literal": null }, "foo"] diff --git a/libs/@local/hashql/mir/tests/ui/reify/null-value.stdout b/libs/@local/hashql/mir/tests/ui/reify/null-value.stdout new file mode 100644 index 00000000000..dd9c5ba6bdd --- /dev/null +++ b/libs/@local/hashql/mir/tests/ui/reify/null-value.stdout @@ -0,0 +1,5 @@ +*thunk foo:0() -> Null { + bb0(): { + return () + } +} \ No newline at end of file