From a7029ef7b6b94d7a28dbe14e4aec67ad3b547790 Mon Sep 17 00:00:00 2001 From: kalsprite Date: Sat, 15 Aug 2026 23:25:13 -0700 Subject: [PATCH] bitset subset --- src/check_expr.cpp | 18 +++--- tests/internal/test_constant_folding.odin | 78 +++++++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tests/internal/test_constant_folding.odin diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ec276884a..344842978 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3343,27 +3343,29 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper case Token_Lt: case Token_LtEq: { + // subset: (lhs & rhs) == lhs. a proper subset also requires lhs != rhs ExactValue lhs = x->value; ExactValue rhs = y->value; - ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs); - res = exact_value_bool(compare_exact_values(op, res, lhs)); + ExactValue both = exact_binary_operator_value(Token_And, lhs, rhs); + bool res = compare_exact_values(Token_CmpEq, both, lhs); if (op == Token_Lt) { - res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs))); + res = res && compare_exact_values(Token_NotEq, lhs, rhs); } - x->value = res; + x->value = exact_value_bool(res); break; } case Token_Gt: case Token_GtEq: { + // superset: (lhs & rhs) == rhs ExactValue lhs = x->value; ExactValue rhs = y->value; - ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs); - res = exact_value_bool(compare_exact_values(op, res, rhs)); + ExactValue both = exact_binary_operator_value(Token_And, lhs, rhs); + bool res = compare_exact_values(Token_CmpEq, both, rhs); if (op == Token_Gt) { - res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs))); + res = res && compare_exact_values(Token_NotEq, lhs, rhs); } - x->value = res; + x->value = exact_value_bool(res); break; } } diff --git a/tests/internal/test_constant_folding.odin b/tests/internal/test_constant_folding.odin new file mode 100644 index 000000000..385ba6835 --- /dev/null +++ b/tests/internal/test_constant_folding.odin @@ -0,0 +1,78 @@ +package test_internal + +import "core:testing" + +// `<=` and `<` on a `bit_set` are subset and proper subset, `>=` and `>` superset. The folder +// asked `(lhs & rhs) <= lhs` where the definition is `(lhs & rhs) == lhs`, which is true for +// any operands, so `<=` folded true unconditionally; `<` compounded it by requiring `lhs < rhs` +// where it needs `lhs != rhs`. Under `when` this decides which declarations exist. + +@(test) +bit_set_subset_folding_matches_runtime :: proc(t: ^testing.T) { + B :: bit_set[0..<4] + + { // disjoint: neither a subset nor a superset + a, b := B{0, 3}, B{0, 1} + testing.expect_value(t, B{0, 3} <= B{0, 1}, a <= b) + testing.expect_value(t, B{0, 3} <= B{0, 1}, false) + testing.expect_value(t, B{0, 3} >= B{0, 1}, a >= b) + testing.expect_value(t, B{0, 3} >= B{0, 1}, false) + } + { // proper subset + a, b := B{0}, B{0, 1} + testing.expect_value(t, B{0} <= B{0, 1}, a <= b) + testing.expect_value(t, B{0} <= B{0, 1}, true) + testing.expect_value(t, B{0} < B{0, 1}, a < b) + testing.expect_value(t, B{0} < B{0, 1}, true) + } + { // equal: a subset but not a proper one + a, b := B{0, 1}, B{0, 1} + testing.expect_value(t, B{0, 1} <= B{0, 1}, a <= b) + testing.expect_value(t, B{0, 1} <= B{0, 1}, true) + testing.expect_value(t, B{0, 1} < B{0, 1}, a < b) + testing.expect_value(t, B{0, 1} < B{0, 1}, false) + } + { // superset + a, b := B{0, 1}, B{0} + testing.expect_value(t, B{0, 1} <= B{0}, a <= b) + testing.expect_value(t, B{0, 1} <= B{0}, false) + testing.expect_value(t, B{0, 1} > B{0}, a > b) + testing.expect_value(t, B{0, 1} > B{0}, true) + } + { // the empty set is a subset of everything, and a proper one unless both are empty + a, b := B{}, B{0} + testing.expect_value(t, B{} < B{0}, a < b) + testing.expect_value(t, B{} < B{0}, true) + } + { + a, b := B{}, B{} + testing.expect_value(t, B{} <= B{}, a <= b) + testing.expect_value(t, B{} <= B{}, true) + testing.expect_value(t, B{} < B{}, a < b) + testing.expect_value(t, B{} < B{}, false) + } + + // equality was never affected, so a fix here must not disturb it + { + a, b := B{0, 1}, B{1, 0} + testing.expect_value(t, B{0, 1} == B{1, 0}, a == b) + testing.expect_value(t, B{0, 1} == B{1, 0}, true) + testing.expect_value(t, B{0, 1} != B{0}, a != B{0}) + } +} + +// a mis-folded subset test selects the wrong `when` arm, which changes which declarations exist +@(test) +bit_set_subset_folding_selects_the_right_when_arm :: proc(t: ^testing.T) { + B :: bit_set[0..<4] + + when (B{0} < B{0, 1}) { W1 :: 1 } else { W1 :: 0 } + when (B{0, 3} <= B{0, 1}) { W2 :: 0 } else { W2 :: 1 } + when (B{0, 1} <= B{0, 1}) { W3 :: 1 } else { W3 :: 0 } + when (B{0, 1} > B{0}) { W4 :: 1 } else { W4 :: 0 } + + testing.expect_value(t, W1, 1) + testing.expect_value(t, W2, 1) + testing.expect_value(t, W3, 1) + testing.expect_value(t, W4, 1) +}