diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3b5baf326..2d77aa229 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -424,6 +424,7 @@ typedef struct bool big_endian : 1; bool little_endian : 1; bool overlap : 1; + bool consecutive : 1; } BitStructDecl; typedef struct VarDecl_ diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 1c4eed31b..07b868644 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1698,6 +1698,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) { CONSUME_OR_RET(TOKEN_LBRACE, false); + bool is_consecutive = false; while (!try_consume(c, TOKEN_RBRACE)) { ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), false); @@ -1713,6 +1714,24 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) SEMA_ERROR_HERE("Expected a field name at this position."); return false; } + if (is_consecutive || tok_is(c, TOKEN_EOS)) + { + if (!is_consecutive) + { + if (decl->bitstruct.members) + { + SEMA_ERROR_HERE("Expected a ':'."); + return false; + } + is_consecutive = true; + } + CONSUME_OR_RET(TOKEN_EOS, false); + unsigned index = vec_size(decl->bitstruct.members); + member_decl->var.start_bit = index; + member_decl->var.end_bit = index; + vec_add(decl->bitstruct.members, member_decl); + continue; + } CONSUME_OR_RET(TOKEN_COLON, false); ASSIGN_EXPR_OR_RET(member_decl->var.start, parse_constant_expr(c), false); if (try_consume(c, TOKEN_DOTDOT)) @@ -1726,7 +1745,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) CONSUME_EOS_OR_RET(false); vec_add(decl->bitstruct.members, member_decl); } - + decl->bitstruct.consecutive = is_consecutive; return true; } /** diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index ded8edc00..7a26b967a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -482,7 +482,8 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl) static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *decl, unsigned index, bool allow_overlap) { - Decl **members = decl->strukt.members; + bool is_consecutive = decl->bitstruct.consecutive; + Decl **members = decl->bitstruct.members; Decl *member = members[index]; // Resolve the type. @@ -511,6 +512,25 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec Int max_bits = (Int) { .type = TYPE_I64, .i = { .low = bits } }; // Resolve the bit range, starting with the beginning + + unsigned start_bit, end_bit; + + if (is_consecutive) + { + if (member_type != type_bool) + { + SEMA_ERROR(member->var.type_info, "For bitstructs without bit ranges, the types must all be 'bool'."); + return false; + } + start_bit = end_bit = member->var.start_bit; + if (start_bit >= bits) + { + SEMA_ERROR(member, "This element would overflow the bitstruct size (%d bits).", bits); + return false; + } + goto AFTER_BITCHECK; + } + Expr *start = member->var.start; if (!sema_analyse_expr(context, start)) return false; @@ -528,7 +548,6 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec return false; } - unsigned start_bit, end_bit; end_bit = start_bit = (unsigned)start->const_expr.ixx.i.low; // Handle the end @@ -567,6 +586,7 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec return false; } + // Check how many bits we need. TypeSize bitsize_type = member_type == type_bool ? 1 : type_size(member_type) * 8; @@ -585,6 +605,7 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec member->var.start_bit = start_bit; member->var.end_bit = end_bit; +AFTER_BITCHECK: // Check for duplicate members. for (unsigned i = 0; i < index; i++) { diff --git a/src/version.h b/src/version.h index 1b95f3101..7f661ba69 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.75" \ No newline at end of file +#define COMPILER_VERSION "0.4.76" \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_simple.c3 b/test/test_suite/bitstruct/bitstruct_simple.c3 new file mode 100644 index 000000000..e9be8ab1c --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_simple.c3 @@ -0,0 +1,18 @@ +bitstruct Foo : char +{ + bool a; + int b; // #error: For bitstructs without bit ranges, the types must all be 'bool' +} + +bitstruct Foo2 : char +{ + bool a0; + bool a1; + bool a2; + bool a3; + bool a4; + bool a5; + bool a6; + bool a7; + bool a8; // #error: overflow +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_simple_err_decl.c3 b/test/test_suite/bitstruct/bitstruct_simple_err_decl.c3 new file mode 100644 index 000000000..e809380b1 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_simple_err_decl.c3 @@ -0,0 +1,11 @@ +bitstruct Foo : char +{ + bool a : 1; + bool b; // #error: Expected a ':' +} + +bitstruct Foo2 : char +{ + bool a; + bool b : 1; // #error: Expected ';' +} \ No newline at end of file diff --git a/test/unit/regression/bitstruct_ops.c3 b/test/unit/regression/bitstruct_ops.c3 index fbbbb9e04..d4ee52c46 100644 --- a/test/unit/regression/bitstruct_ops.c3 +++ b/test/unit/regression/bitstruct_ops.c3 @@ -1,4 +1,4 @@ -module test; +module bitstruct_ops; import std::io; bitstruct Foo : int @@ -41,3 +41,4 @@ fn void! test_bitops() @test b3 ^= Bar { true, true, false }; assert(b3.z == true && b3.w == false && b3.gh == true); } + diff --git a/test/unit/regression/bitstruct_ops2.c3 b/test/unit/regression/bitstruct_ops2.c3 new file mode 100644 index 000000000..e4b2e74b2 --- /dev/null +++ b/test/unit/regression/bitstruct_ops2.c3 @@ -0,0 +1,44 @@ +module bitstruct_ops_bool; +import std::io; + +bitstruct Foo : int +{ + bool a; + bool b; +} + +bitstruct Bar : char[13] +{ + bool z; + bool w; + bool gh; +} + +fn void! test_bitops() @test +{ + Foo f1 = { true, true }; + Foo f2 = { true, false }; + + Foo f3 = f1 & f2; + assert(f3.a == true); + assert(f3.b == false); + + Foo f4 = (f1 | ~f2) ^ f3; + assert(f4.a == false && f4.b == true); + Foo f5 = Foo { true, false } | Foo { false, true }; + assert(f5.a == true && f5.b == true); + + f5 &= f2; + assert(f5.a == true && f5.b == false); + + Bar b1 = { true, true, true }; + Bar b2 = { true, false, false }; + + Bar b3 = b1 & b2; + assert(b3.z == true && b3.w == false && b3.gh == false); + b3 = ~b3; + assert(b3.z == false && b3.w == true && b3.gh == true); + b3 ^= Bar { true, true, false }; + assert(b3.z == true && b3.w == false && b3.gh == true); +} +