diff --git a/releasenotes.md b/releasenotes.md index e5af31a9d..940c50e88 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Compiler segfault when using bitwise not on number literal cast to bitstruct #2373. - Formatter did not properly handle "null" for any, and null for empty faults. #2375 - Bitstructs no longer overloadable with bitops. #2374 +- types::has_equals fails with assert for bitstructs #2377 ### Stdlib changes - Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`. diff --git a/src/compiler/number.c b/src/compiler/number.c index 01273a5a1..b7ec49d43 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -127,6 +127,55 @@ INLINE ConstInitializer *expr_const_array_init_at(ConstInitializer *init, ArrayS } UNREACHABLE; } + +INLINE void swap_to_zero_to_left(ConstInitializer **left_ref, ConstInitializer **right_ref) +{ + ConstInitializer *right = *right_ref; + if (right->kind == CONST_INIT_ZERO) + { + *right_ref = *left_ref; + *left_ref = right; + } +} + + +static bool expr_const_compare_bitstruct(const ExprConst *left, const ExprConst *right, BinaryOp op) +{ + ConstInitializer *lhs = left->initializer; + ConstInitializer *rhs = right->initializer; + swap_to_zero_to_left(&lhs, &rhs); + bool find_eq = op == BINARYOP_EQ; + if (lhs->kind == CONST_INIT_ZERO) return const_init_is_zero(rhs) ? find_eq : !find_eq; + ConstInitializer **lhs_inits = lhs->init_struct; + ConstInitializer **rhs_inits = rhs->init_struct; + Decl **members = lhs->type->decl->strukt.members; + unsigned len = vec_size(members); + for (unsigned i = 0; i < len; i++) + { + ConstInitializer *init_lhs = lhs_inits[i]; + ConstInitializer *init_rhs = rhs_inits[i]; + swap_to_zero_to_left(&init_lhs, &init_rhs); + // Zero case + if (lhs->kind == CONST_INIT_ZERO) + { + if (const_init_is_zero(rhs)) continue; + return !find_eq; + } + // Both should have values + Expr *lhs_expr = init_lhs->init_value; + Expr *rhs_expr = init_rhs->init_value; + + bool to_const = sema_cast_const(lhs_expr) && sema_cast_const(rhs_expr); + assert(to_const); + if (!expr_const_compare(&lhs_expr->const_expr, &rhs_expr->const_expr, BINARYOP_EQ)) + { + return !find_eq; + } + } + return find_eq; +} + + bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op) { bool is_eq; @@ -252,6 +301,7 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp if (a_is_zero != b_is_zero) goto MISMATCH; continue; } + assert(a && b); assert(b->kind == CONST_INIT_VALUE && a->kind == CONST_INIT_VALUE); Expr *a_value = a->init_value; Expr *b_value = b->init_value; @@ -262,6 +312,10 @@ MISMATCH: } return op == BINARYOP_EQ; } + if (left->initializer->type->type_kind == TYPE_BITSTRUCT) + { + return expr_const_compare_bitstruct(left, right, op); + } FALLTHROUGH; case CONST_SLICE: case CONST_UNTYPED_LIST: diff --git a/test/test_suite/bitstruct/bitstruct_const_compare.c3 b/test/test_suite/bitstruct/bitstruct_const_compare.c3 new file mode 100644 index 000000000..42d5f6580 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_const_compare.c3 @@ -0,0 +1,36 @@ +module test; + +bitstruct Foo : int +{ + bool foo; +} + +bitstruct Foo2 : int +{ + bool a : 1; + int b : 7..9; +} + +fn void main() +{ + var $foo = (Foo) { true }; + var $bar = (Foo) { }; + bool $baz = $foo == $bar; + $assert($baz == false); + $baz = $foo == $foo; + $assert($baz == true); + $assert(true == types::has_equals(Foo)); + Foo2 $a1 = { true, 2 }; + Foo2 $a2 = {}; + Foo2 $a3 = { true, 3 }; + Foo2 $a4 = { false, 0 }; + $assert(!($a1 == $a2)); + $assert($a1 != $a2); + $assert($a2 == $a2); + $assert(!($a2 != $a2)); + $assert($a1 != $a3); + $assert(!($a1 == $a3)); + $assert($a4 == $a2); + $assert(!($a4 != $a2)); + +} \ No newline at end of file