diff options
| author | gingerBill <bill@gingerbill.org> | 2024-01-28 17:33:29 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-01-28 17:33:29 +0000 |
| commit | 68df35b378d59f9813f5af81e61080c5f1b20e23 (patch) | |
| tree | 9c04917bbbbba20c9270ed5442a1f32a828359fd | |
| parent | c1d853a24e69689a40668c4aa036312bc871540c (diff) | |
Add `#field_align(N)`
It sets the minimum alignment for the fields within a struct. This cannot be used with `#packed`, but can be used with `#align(N)`.
If `#align(N)` is less than `#field_align(N)`, then a warning will be printed.
| -rw-r--r-- | core/odin/ast/ast.odin | 1 | ||||
| -rw-r--r-- | core/odin/parser/parser.odin | 7 | ||||
| -rw-r--r-- | src/check_type.cpp | 40 | ||||
| -rw-r--r-- | src/parser.cpp | 25 | ||||
| -rw-r--r-- | src/parser.hpp | 1 | ||||
| -rw-r--r-- | src/types.cpp | 14 |
6 files changed, 64 insertions, 24 deletions
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 67a26d6f2..f6bcbab4e 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -768,6 +768,7 @@ Struct_Type :: struct { tok_pos: tokenizer.Pos, poly_params: ^Field_List, align: ^Expr, + field_align: ^Expr, where_token: tokenizer.Token, where_clauses: []^Expr, is_packed: bool, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 3383f3514..fc7a2c792 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2547,6 +2547,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { poly_params: ^ast.Field_List align: ^ast.Expr + field_align: ^ast.Expr is_packed: bool is_raw_union: bool is_no_copy: bool @@ -2578,6 +2579,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) } align = parse_expr(p, true) + case "field_align": + if field_align != nil { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + field_align = parse_expr(p, true) case "raw_union": if is_raw_union { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) @@ -2620,6 +2626,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)) st.poly_params = poly_params st.align = align + st.field_align = field_align st.is_packed = is_packed st.is_raw_union = is_raw_union st.is_no_copy = is_no_copy diff --git a/src/check_type.cpp b/src/check_type.cpp index a95026711..5cb1eb9cc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -219,13 +219,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit } -gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { +gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, char const *msg) { GB_ASSERT(align_ != nullptr); Operand o = {}; check_expr(ctx, &o, node); if (o.mode != Addressing_Constant) { if (o.mode != Addressing_Invalid) { - error(node, "#align must be a constant"); + error(node, "#%s must be a constant", msg); } return false; } @@ -237,13 +237,13 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) if (v.used > 1) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &v); - error(node, "#align too large, %.*s", LIT(str)); + error(node, "#%s too large, %.*s", msg, LIT(str)); gb_free(a, str.text); return false; } i64 align = big_int_to_i64(&v); if (align < 1 || !gb_is_power_of_two(cast(isize)align)) { - error(node, "#align must be a power of 2, got %lld", align); + error(node, "#%s must be a power of 2, got %lld", msg, align); return false; } *align_ = align; @@ -251,7 +251,7 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) } } - error(node, "#align must be an integer"); + error(node, "#%s must be an integer", msg); return false; } @@ -645,16 +645,26 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); } - if (st->align != nullptr) { - if (st->is_packed) { - syntax_error(st->align, "'#align' cannot be applied with '#packed'"); - return; - } - i64 custom_align = 1; - if (check_custom_align(ctx, st->align, &custom_align)) { - struct_type->Struct.custom_align = custom_align; - } +#define ST_ALIGN(_name) if (st->_name != nullptr) { \ + if (st->is_packed) { \ + syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \ + return; \ + } \ + i64 align = 1; \ + if (check_custom_align(ctx, st->_name, &align, #_name)) { \ + struct_type->Struct.custom_##_name = align; \ + } \ } + + ST_ALIGN(field_align); + ST_ALIGN(align); + if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) { + warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)", + cast(long long)struct_type->Struct.custom_align, + cast(long long)struct_type->Struct.custom_field_align); + } + +#undef ST_ALIGN } gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_union(union_type)); @@ -746,7 +756,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no if (ut->align != nullptr) { i64 custom_align = 1; - if (check_custom_align(ctx, ut->align, &custom_align)) { + if (check_custom_align(ctx, ut->align, &custom_align, "align")) { if (variants.count == 0) { error(ut->align, "An empty union cannot have a custom alignment"); } else { diff --git a/src/parser.cpp b/src/parser.cpp index 2671054df..b16a88de5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -383,10 +383,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f); break; case Ast_StructType: - n->StructType.fields = clone_ast_array(n->StructType.fields, f); + n->StructType.fields = clone_ast_array(n->StructType.fields, f); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f); - n->StructType.align = clone_ast(n->StructType.align, f); - n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f); + n->StructType.align = clone_ast(n->StructType.align, f); + n->StructType.field_align = clone_ast(n->StructType.field_align, f); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants, f); @@ -1125,7 +1126,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, - Ast *align, + Ast *align, Ast *field_align, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; @@ -1136,6 +1137,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i result->StructType.is_raw_union = is_raw_union; result->StructType.is_no_copy = is_no_copy; result->StructType.align = align; + result->StructType.field_align = field_align; result->StructType.where_token = where_token; result->StructType.where_clauses = slice_from_array(where_clauses); return result; @@ -2507,6 +2509,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_raw_union = false; bool no_copy = false; Ast *align = nullptr; + Ast *field_align = nullptr; if (allow_token(f, Token_OpenParen)) { isize param_count = 0; @@ -2543,6 +2546,18 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { error_line("\tSuggestion: #align(%s)", s); gb_string_free(s); } + } else if (tag.string == "field_align") { + if (field_align) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + field_align = parse_expr(f, true); + if (field_align && field_align->kind != Ast_ParenExpr) { + ERROR_BLOCK(); + gbString s = expr_to_string(field_align); + syntax_warning(tag, "#field_align requires parentheses around the expression"); + error_line("\tSuggestion: #field_align(%s)", s); + gb_string_free(s); + } } else if (tag.string == "raw_union") { if (is_raw_union) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); @@ -2591,7 +2606,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses); } break; case Token_union: { diff --git a/src/parser.hpp b/src/parser.hpp index cc1836ef3..1edb1f9dd 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -713,6 +713,7 @@ AST_KIND(_TypeBegin, "", bool) \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ + Ast *field_align; \ Token where_token; \ Slice<Ast *> where_clauses; \ bool is_packed; \ diff --git a/src/types.cpp b/src/types.cpp index 2f39d5caa..b99d469e4 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -137,6 +137,7 @@ struct TypeStruct { Scope * scope; i64 custom_align; + i64 custom_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; @@ -3668,10 +3669,15 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); } -gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) { +gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) { gbAllocator a = permanent_allocator(); auto offsets = gb_alloc_array(a, i64, fields.count); i64 curr_offset = 0; + + if (min_field_align == 0) { + min_field_align = 1; + } + if (is_raw_union) { for_array(i, fields) { offsets[i] = 0; @@ -3692,7 +3698,7 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack offsets[i] = -1; } else { Type *t = fields[i]->type; - i64 align = gb_max(type_align_of(t), 1); + i64 align = gb_max(type_align_of(t), min_field_align); i64 size = gb_max(type_size_of( t), 0); curr_offset = align_formula(curr_offset, align); offsets[i] = curr_offset; @@ -3709,7 +3715,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Struct.offset_mutex); if (!t->Struct.are_offsets_set) { t->Struct.are_offsets_being_processed = true; - t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union); + t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align); t->Struct.are_offsets_being_processed = false; t->Struct.are_offsets_set = true; return true; @@ -3718,7 +3724,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Tuple.mutex); if (!t->Tuple.are_offsets_set) { t->Tuple.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1); t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; |