on master help sign in
Boolean tests return a one-element relation (Rel Bool)

any, all, elem now yield a one-element relation, matching the aggregates: a query result that composes, extracted with run. Consistent with count/sum/etc. Each in-memory path wraps the Bool in knot_relation_singleton (any/all via define_stdlib_fn_2_rel, elem via the shared knot_list_elem); the EXISTS SQL pushdown terminal wraps it too. Tests that used these as bare Bools now expect [True]/[False]. Suite 289/0.

commit1244f8edd726f25fe26234347e9ae47193f03b7f authoriko <mail@iko.soy> dateSat 29 Aug 2026, 16:06 (1 month ago) parentc8c1cd9
4 files changed+29−21
crates/knot-compiler/src/codegen.rs+12−8 crates/knot-compiler/src/infer.rs+7−6 crates/knot-jit/tests/numeric_text.rs+3−2 crates/knot-jit/tests/relations.rs+7−5
crates/knot-compiler/src/codegen.rs+12−8
@@ -3439,7 +3439,7 @@ impl<M: cranelift_module::Module> Codegen<M> {
34393439 self.define_stdlib_fn_2("contains", "knot_text_contains", false);
34403440 self.define_stdlib_fn_2("startsWith", "knot_text_starts_with", false);
34413441 self.define_stdlib_fn_2("endsWith", "knot_text_ends_with", false);
3442− self.define_stdlib_fn_2("elem", "knot_list_elem", false);
3442+ self.define_stdlib_fn_2_rel("elem", "knot_list_elem", false);
34433443 self.define_stdlib_fn_2("diff", "knot_relation_diff", true);
34443444 self.define_stdlib_fn_2("inter", "knot_relation_inter", true);
34453445 self.define_stdlib_sum();
@@ -3454,8 +3454,8 @@ impl<M: cranelift_module::Module> Codegen<M> {
34543454 self.define_stdlib_fn_2_rel("minOn", "knot_relation_min", true);
34553455 self.define_stdlib_fn_2_rel("maxOn", "knot_relation_max", true);
34563456 self.define_stdlib_fn_2_rel("countWhere", "knot_relation_count_where", true);
3457− self.define_stdlib_fn_2("any", "knot_relation_any", true);
3458− self.define_stdlib_fn_2("all", "knot_relation_all", true);
3457+ self.define_stdlib_fn_2_rel("any", "knot_relation_any", true);
3458+ self.define_stdlib_fn_2_rel("all", "knot_relation_all", true);
34593459
34603460 // List ADT builtins (`base.list.*`). Order-preserving sequence ops,
34613461 // runtime-implemented (knot_list_*). Higher-order ones (map/filter/
@@ -16489,11 +16489,15 @@ impl<M: cranelift_module::Module> Codegen<M> {
1648916489 // count : Rel a -> Rel Int — a one-element relation.
1649016490 self.call_rt(builder, "knot_relation_singleton", &[n])
1649116491 }
16492− QueryTerminal::Exists { .. } => self.call_rt(
16493− builder,
16494− "knot_source_query_exists",
16495− &[db, sql_ptr, sql_len, params_rel],
16496− ),
16492+ QueryTerminal::Exists { .. } => {
16493+ let b = self.call_rt(
16494+ builder,
16495+ "knot_source_query_exists",
16496+ &[db, sql_ptr, sql_len, params_rel],
16497+ );
16498+ // any/all/elem : ... -> Rel Bool — a one-element relation.
16499+ self.call_rt(builder, "knot_relation_singleton", &[b])
16500+ }
1649716501 QueryTerminal::Aggregate { result_flag, .. } => {
1649816502 let rt_fn = query.runtime_fn();
1649916503 // SUM and MIN/MAX take an extra I64 flag (is_float / is_text);
crates/knot-compiler/src/infer.rs+7−6
@@ -11812,7 +11812,8 @@ impl Infer {
1181211812 );
1181311813 }
1181411814
11815− // any : ∀a. (a -> Bool) -> [a] -> Bool
11815+ // any : ∀a. (a -> Bool) -> [a] -> Rel Bool (a one-element relation,
11816+ // like the aggregates — a query result, not an immediate extraction).
1181611817 {
1181711818 let a = self.fresh_var();
1181811819 self.bind_top(
@@ -11823,14 +11824,14 @@ impl Infer {
1182311824 Box::new(Ty::Fun(Box::new(Ty::Var(a)), Box::new(Ty::Bool))),
1182411825 Box::new(Ty::Fun(
1182511826 Box::new(Ty::Relation(Box::new(Ty::Var(a)))),
11826− Box::new(Ty::Bool),
11827+ Box::new(Ty::Relation(Box::new(Ty::Bool))),
1182711828 )),
1182811829 ),
1182911830 ),
1183011831 );
1183111832 }
1183211833
11833− // all : ∀a. (a -> Bool) -> [a] -> Bool
11834+ // all : ∀a. (a -> Bool) -> [a] -> Rel Bool (one-element relation, like any)
1183411835 {
1183511836 let a = self.fresh_var();
1183611837 self.bind_top(
@@ -11841,7 +11842,7 @@ impl Infer {
1184111842 Box::new(Ty::Fun(Box::new(Ty::Var(a)), Box::new(Ty::Bool))),
1184211843 Box::new(Ty::Fun(
1184311844 Box::new(Ty::Relation(Box::new(Ty::Var(a)))),
11844− Box::new(Ty::Bool),
11845+ Box::new(Ty::Relation(Box::new(Ty::Bool))),
1184511846 )),
1184611847 ),
1184711848 ),
@@ -11931,7 +11932,7 @@ impl Infer {
1193111932 )),
1193211933 );
1193311934
11934− // elem : ∀a. a -> [a] -> Bool
11935+ // elem : ∀a. a -> [a] -> Rel Bool (one-element relation, like any/all)
1193511936 let a = self.fresh_var();
1193611937 self.bind_top(
1193711938 "elem",
@@ -11941,7 +11942,7 @@ impl Infer {
1194111942 Box::new(Ty::Var(a)),
1194211943 Box::new(Ty::Fun(
1194311944 Box::new(Ty::Relation(Box::new(Ty::Var(a)))),
11944− Box::new(Ty::Bool),
11945+ Box::new(Ty::Relation(Box::new(Ty::Bool))),
1194511946 )),
1194611947 ),
1194711948 ),
crates/knot-jit/tests/numeric_text.rs+3−2
@@ -110,8 +110,9 @@ fn upsert_by_replaces_or_appends() {
110110
111111#[test]
112112fn elem_membership() {
113− assert_show("base.elem 2 [1 2 3]", "True");
114− assert_show("base.elem 9 [1 2 3]", "False");
113+ // elem is a one-element relation now (Rel Bool), so it shows as [True].
114+ assert_show("base.elem 2 [1 2 3]", "[True]");
115+ assert_show("base.elem 9 [1 2 3]", "[False]");
115116}
116117
117118#[test]
crates/knot-jit/tests/relations.rs+7−5
@@ -154,19 +154,21 @@ fn diff_relations() {
154154
155155#[test]
156156fn elem_present() {
157− assert_show("base.elem 2 [1 2 3]", "True");
157+ // elem is a one-element relation now (Rel Bool).
158+ assert_show("base.elem 2 [1 2 3]", "[True]");
158159}
159160
160161#[test]
161162fn elem_absent() {
162− assert_show("base.elem 9 [1 2 3]", "False");
163+ assert_show("base.elem 9 [1 2 3]", "[False]");
163164}
164165
165166#[test]
166167fn any_all() {
167− assert_show("base.any (\\n -> n > 2) [1 2 3]", "True");
168− assert_show("base.all (\\n -> n > 0) [1 2 3]", "True");
169− assert_show("base.all (\\n -> n > 1) [1 2 3]", "False");
168+ // any/all are one-element relations now (Rel Bool).
169+ assert_show("base.any (\\n -> n > 2) [1 2 3]", "[True]");
170+ assert_show("base.all (\\n -> n > 0) [1 2 3]", "[True]");
171+ assert_show("base.all (\\n -> n > 1) [1 2 3]", "[False]");
170172}
171173
172174// ── ordering / slicing (sorted relations have a fixed iteration order) ───