diff --git a/releasenotes.md b/releasenotes.md index 869796465..086052adf 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -152,6 +152,8 @@ - Added posix socket functions. ### Fixes +- Anonymous bitstructs check of duplicate member names fixed. +- Assignment to anonymous bitstruct members in structs. - Fix casts on empty initializers. - Fix to DString reserve - @local declarations in generic modules available by accident. diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 746bce913..1b558aa40 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -737,13 +737,14 @@ typedef enum ATTR_FAULT = 1 << 7, ATTR_DEF = 1 << 8, ATTR_MEMBER = 1 << 9, - ATTR_INTERFACE = 1 << 10, - ATTR_CALL = 1 << 11, - ATTR_BITSTRUCT = 1 << 12, - ATTR_MACRO = 1 << 13, - ATTR_INITIALIZER = 1 << 14, - ATTR_FINALIZER = 1 << 15, - ATTR_DEFINE = 1 << 16, + ATTR_BITSTRUCT_MEMBER = 1 << 10, + ATTR_INTERFACE = 1 << 11, + ATTR_CALL = 1 << 12, + ATTR_BITSTRUCT = 1 << 13, + ATTR_MACRO = 1 << 14, + ATTR_INITIALIZER = 1 << 15, + ATTR_FINALIZER = 1 << 16, + ATTR_DEFINE = 1 << 17, ATTR_XXLIZER = ATTR_INITIALIZER | ATTR_FINALIZER } AttributeDomain; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index af25b823d..c1ea093a7 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1105,9 +1105,9 @@ static inline void llvm_emit_bitassign_expr(GenContext *c, BEValue *be_value, Ex // Grab the parent BEValue parent; - llvm_emit_expr(c, &parent, parent_expr); - Decl *member = lhs->access_expr.ref; + llvm_emit_expr(c, &parent, parent_expr); + llvm_emit_bitstruct_member(c, &parent, type_flatten(parent_expr->type)->decl, member); // If we have assign + op, load the current value, perform the operation. if (expr->binary_expr.operator != BINARYOP_ASSIGN) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 66e7cfb35..a7bcc6997 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -28,10 +28,10 @@ static inline const char *method_name_by_decl(Decl *method_like); static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *erase_decl); static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase_decl); -static bool sema_analyse_union_members(SemaContext *context, Decl *decl, Decl **members); -static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl **members); +static bool sema_analyse_union_members(SemaContext *context, Decl *decl); +static bool sema_analyse_struct_members(SemaContext *context, Decl *decl); static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent, Decl *decl, bool *erase_decl); -static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *decl, unsigned index, bool allow_overlap); +static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *parent, Decl *member, unsigned index, bool allow_overlap, bool *erase_decl); static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extra_params, bool *pure_ref); @@ -207,13 +207,14 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent } } -static bool sema_analyse_union_members(SemaContext *context, Decl *decl, Decl **members) +static bool sema_analyse_union_members(SemaContext *context, Decl *decl) { AlignSize max_size = 0; MemberIndex max_alignment_element = 0; AlignSize max_alignment = 0; bool has_named_parameter = false; + Decl **members = decl->strukt.members; unsigned member_count = vec_size(members); for (unsigned i = 0; i < member_count; i++) { @@ -308,7 +309,7 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl, Decl ** return true; } -static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl **members) +static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) { // Default alignment is 1 even if it is empty. AlignSize natural_alignment = 1; @@ -316,8 +317,8 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl * AlignSize size = 0; AlignSize offset = 0; bool is_packed = decl->is_packed; - unsigned member_count = vec_size(members); Decl **struct_members = decl->strukt.members; + unsigned member_count = vec_size(struct_members); for (unsigned i = 0; i < member_count; i++) { @@ -481,11 +482,11 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *er Decl** state = sema_decl_stack_store(); if (decl->decl_kind == DECL_UNION) { - success = sema_analyse_union_members(context, decl, decl->strukt.members); + success = sema_analyse_union_members(context, decl); } else { - success = sema_analyse_struct_members(context, decl, decl->strukt.members); + success = sema_analyse_struct_members(context, decl); } sema_decl_stack_restore(state); } @@ -493,11 +494,11 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *er { if (decl->decl_kind == DECL_UNION) { - success = sema_analyse_union_members(context, decl, decl->strukt.members); + success = sema_analyse_union_members(context, decl); } else { - success = sema_analyse_struct_members(context, decl, decl->strukt.members); + success = sema_analyse_struct_members(context, decl); } } DEBUG_LOG("Struct/union size %d, alignment %d.", (int)decl->strukt.size, (int)decl->alignment); @@ -506,11 +507,38 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *er return decl_ok(decl); } -static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *decl, unsigned index, bool allow_overlap) +static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *parent, Decl *member, unsigned index, bool allow_overlap, bool *erase_decl) { - bool is_consecutive = decl->bitstruct.consecutive; - Decl **members = decl->bitstruct.members; - Decl *member = members[index]; + + if (member->resolve_status == RESOLVE_DONE) + { + if (!decl_ok(member)) return false; + if (member->name) sema_decl_stack_push(member); + return true; + } + + if (member->resolve_status == RESOLVE_RUNNING) + { + RETURN_SEMA_ERROR(member, "Circular dependency resolving member."); + } + + bool ease_decl = false; + if (!sema_analyse_attributes(context, member, member->attributes, ATTR_BITSTRUCT_MEMBER, erase_decl)) return decl_poison(member); + if (*erase_decl) return true; + + if (member->name) + { + Decl *other = sema_decl_stack_resolve_symbol(member->name); + if (other) + { + SEMA_ERROR(member, "Duplicate member name '%s'.", other->name); + SEMA_NOTE(other, "Previous declaration was here."); + return false; + } + if (member->name) sema_decl_stack_push(member); + } + + bool is_consecutive = parent->bitstruct.consecutive; // Resolve the type. if (!sema_resolve_type_info(context, member->var.type_info)) return false; @@ -528,11 +556,11 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec } // Grab the underlying bit type size. - BitSize bits = type_size(decl->bitstruct.base_type->type) * (BitSize)8; + BitSize bits = type_size(parent->bitstruct.base_type->type) * (BitSize)8; if (bits > MAX_BITSTRUCT) { - SEMA_ERROR(decl->bitstruct.base_type, "Bitstruct size may not exceed %d bits.", MAX_BITSTRUCT); + SEMA_ERROR(parent->bitstruct.base_type, "Bitstruct size may not exceed %d bits.", MAX_BITSTRUCT); return false; } Int max_bits = (Int) { .type = TYPE_I64, .i = { .low = bits } }; @@ -632,26 +660,21 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec member->var.end_bit = end_bit; AFTER_BIT_CHECK: - // Check for duplicate members. - for (unsigned i = 0; i < index; i++) + // Check for overlap + if (!allow_overlap) { - Decl *other_member = members[i]; - if (member->name == other_member->name) + Decl **members = parent->bitstruct.members; + for (unsigned i = 0; i < index; i++) { - SEMA_ERROR(member, "Duplicate members with the name '%s'.", member->name); - SEMA_NOTE(other_member, "The other member was declared here."); - return false; - } - // And possibly overlap. - if (allow_overlap) continue; - - // Check for overlap. - if ((start_bit >= other_member->var.start_bit || end_bit >= other_member->var.start_bit) - && start_bit <= other_member->var.end_bit) - { - SEMA_ERROR(member, "Overlapping members, please use '@overlap' if this is intended."); - SEMA_NOTE(other_member, "The other member was declared here."); - return false; + Decl *other_member = members[i]; + // Check for overlap. + if ((start_bit >= other_member->var.start_bit || end_bit >= other_member->var.start_bit) + && start_bit <= other_member->var.end_bit) + { + SEMA_ERROR(member, "Overlapping members, please use '@overlap' if this is intended."); + SEMA_NOTE(other_member, "The other member was declared here."); + return false; + } } } member->resolve_status = RESOLVE_DONE; @@ -673,14 +696,29 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase return false; } Decl **members = decl->bitstruct.members; - VECEACH(members, i) + unsigned member_count = vec_size(members); + + Decl **state = decl->name ? sema_decl_stack_store() : NULL; + for (unsigned i = 0; i < member_count; i++) { - if (!sema_analyse_bitstruct_member(context, decl, i, decl->bitstruct.overlap)) + AGAIN:; + Decl *member = members[i]; + if (!decl_ok(member)) goto ERROR; + bool erase_decl_member = false; + if (!sema_analyse_bitstruct_member(context, decl, member, i, decl->bitstruct.overlap, &erase_decl_member)) goto ERROR; + if (erase_decl_member) { - return decl_poison(decl); + vec_erase_ptr_at(members, i); + member_count--; + if (i < member_count) goto AGAIN; + break; } } + if (state) sema_decl_stack_restore(state); return true; +ERROR: + if (state) sema_decl_stack_restore(state); + return decl_poison(decl); } @@ -1544,6 +1582,8 @@ static const char *attribute_domain_to_string(AttributeDomain domain) return "interface"; case ATTR_MEMBER: return "member"; + case ATTR_BITSTRUCT_MEMBER: + return "bitstruct member"; case ATTR_FUNC: return "function"; case ATTR_GLOBAL: @@ -1626,7 +1666,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_BUILTIN] = ATTR_MACRO | ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST, [ATTRIBUTE_CALLCONV] = ATTR_FUNC, - [ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | ATTR_FUNC | ATTR_MACRO | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER, + [ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | ATTR_FUNC | ATTR_MACRO | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER | ATTR_BITSTRUCT_MEMBER, [ATTRIBUTE_DYNAMIC] = ATTR_FUNC, [ATTRIBUTE_EXPORT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES, [ATTRIBUTE_EXTERN] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, diff --git a/src/version.h b/src/version.h index 56e5585b6..76ff0a4c1 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.595" \ No newline at end of file +#define COMPILER_VERSION "0.4.596" \ No newline at end of file diff --git a/test/test_suite/bitstruct/anon_bitstruct_name_overlap.c3 b/test/test_suite/bitstruct/anon_bitstruct_name_overlap.c3 new file mode 100644 index 000000000..6390fb585 --- /dev/null +++ b/test/test_suite/bitstruct/anon_bitstruct_name_overlap.c3 @@ -0,0 +1,24 @@ +import std::io; + +struct Test { +ushort a; + bitstruct : ushort @overlap { + ushort ab : 0..15; + char a : 8..15; // #error: Duplicate member name 'a' + char b : 0..7; + bool c : 7; + bool d : 6; + bool e : 5; + bool f : 4; + } +} + +fn void main() { + io::printfn("Weird structs :P"); + + Test test; + test.ab = 0xAFBA; + + io::printfn("%02x %02x -> %04x\n", test.a, test.b, test.ab); + io::printfn("%x %x %x %x\n", test.c, test.d, test.e, test.f); +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_anon_in_struct_ok.c3t b/test/test_suite/bitstruct/bitstruct_anon_in_struct_ok.c3t new file mode 100644 index 000000000..5396f2b62 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_anon_in_struct_ok.c3t @@ -0,0 +1,122 @@ +// #target: macos-x64 +module foo; +import std::io; + +struct Test { +ushort afff; + bitstruct : ushort @overlap { + ushort ab : 0..15; + char a : 8..15; + char b : 0..7; + bool c : 7; + bool d : 6; + bool e : 5; + bool f : 4; + } +} + +fn void main() { + + + Test test; + test.ab = 0xAFBA; + + io::printfn("%02x %02x -> %04x\n", test.a, test.b, test.ab); + io::printfn("%x %x %x %x\n", test.c, test.d, test.e, test.f); +} + +/* #expect: foo.ll + + +define void @foo.main() #0 { +entry: + %test = alloca %Test, align 2 + %retparam = alloca i64, align 8 + %varargslots = alloca [3 x %any], align 16 + %taddr = alloca i8, align 1 + %taddr2 = alloca i8, align 1 + %taddr3 = alloca i16, align 2 + %retparam4 = alloca i64, align 8 + %varargslots5 = alloca [4 x %any], align 16 + %taddr8 = alloca i8, align 1 + %taddr11 = alloca i8, align 1 + %taddr14 = alloca i8, align 1 + %taddr17 = alloca i8, align 1 + %0 = getelementptr inbounds %Test, ptr %test, i32 0, i32 0 + store i16 0, ptr %0, align 2 + %1 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + store i16 0, ptr %1, align 2 + %2 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %3 = load i16, ptr %2, align 2 + store i16 -20550, ptr %2, align 2 + %4 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %5 = load i16, ptr %4, align 2 + %lshrl = lshr i16 %5, 8 + %6 = and i16 255, %lshrl + %trunc = trunc i16 %6 to i8 + store i8 %trunc, ptr %taddr, align 1 + %7 = insertvalue %any undef, ptr %taddr, 0 + %8 = insertvalue %any %7, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + %9 = getelementptr inbounds [3 x %any], ptr %varargslots, i64 0, i64 0 + store %any %8, ptr %9, align 16 + %10 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %11 = load i16, ptr %10, align 2 + %12 = and i16 255, %11 + %trunc1 = trunc i16 %12 to i8 + store i8 %trunc1, ptr %taddr2, align 1 + %13 = insertvalue %any undef, ptr %taddr2, 0 + %14 = insertvalue %any %13, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + %15 = getelementptr inbounds [3 x %any], ptr %varargslots, i64 0, i64 1 + store %any %14, ptr %15, align 16 + %16 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %17 = load i16, ptr %16, align 2 + store i16 %17, ptr %taddr3, align 2 + %18 = insertvalue %any undef, ptr %taddr3, 0 + %19 = insertvalue %any %18, i64 ptrtoint (ptr @"$ct.ushort" to i64), 1 + %20 = getelementptr inbounds [3 x %any], ptr %varargslots, i64 0, i64 2 + store %any %19, ptr %20, align 16 + %21 = call i64 @std.io.printfn(ptr %retparam, ptr @.str, i64 18, ptr %varargslots, i64 3) + %22 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %23 = load i16, ptr %22, align 2 + %lshrl6 = lshr i16 %23, 7 + %24 = and i16 1, %lshrl6 + %trunc7 = trunc i16 %24 to i8 + store i8 %trunc7, ptr %taddr8, align 1 + %25 = insertvalue %any undef, ptr %taddr8, 0 + %26 = insertvalue %any %25, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + %27 = getelementptr inbounds [4 x %any], ptr %varargslots5, i64 0, i64 0 + store %any %26, ptr %27, align 16 + %28 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %29 = load i16, ptr %28, align 2 + %lshrl9 = lshr i16 %29, 6 + %30 = and i16 1, %lshrl9 + %trunc10 = trunc i16 %30 to i8 + store i8 %trunc10, ptr %taddr11, align 1 + %31 = insertvalue %any undef, ptr %taddr11, 0 + %32 = insertvalue %any %31, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + %33 = getelementptr inbounds [4 x %any], ptr %varargslots5, i64 0, i64 1 + store %any %32, ptr %33, align 16 + %34 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %35 = load i16, ptr %34, align 2 + %lshrl12 = lshr i16 %35, 5 + %36 = and i16 1, %lshrl12 + %trunc13 = trunc i16 %36 to i8 + store i8 %trunc13, ptr %taddr14, align 1 + %37 = insertvalue %any undef, ptr %taddr14, 0 + %38 = insertvalue %any %37, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + %39 = getelementptr inbounds [4 x %any], ptr %varargslots5, i64 0, i64 2 + store %any %38, ptr %39, align 16 + %40 = getelementptr inbounds %Test, ptr %test, i32 0, i32 1 + %41 = load i16, ptr %40, align 2 + %lshrl15 = lshr i16 %41, 4 + %42 = and i16 1, %lshrl15 + %trunc16 = trunc i16 %42 to i8 + store i8 %trunc16, ptr %taddr17, align 1 + %43 = insertvalue %any undef, ptr %taddr17, 0 + %44 = insertvalue %any %43, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + %45 = getelementptr inbounds [4 x %any], ptr %varargslots5, i64 0, i64 3 + store %any %44, ptr %45, align 16 + %46 = call i64 @std.io.printfn(ptr %retparam4, ptr @.str.1, i64 12, ptr %varargslots5, i64 4) + ret void +} + diff --git a/test/test_suite/cast/cast_untyped_list_error.c3 b/test/test_suite/cast/cast_untyped_list_error.c3 index afcb9153b..2bad2c257 100644 --- a/test/test_suite/cast/cast_untyped_list_error.c3 +++ b/test/test_suite/cast/cast_untyped_list_error.c3 @@ -2,5 +2,5 @@ module testing; fn void main() { - (void){}; // #error: foekfoe + (void){}; // #error: cannot use compound literal initialization } \ No newline at end of file