aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-11-05 13:51:12 +0000
committerGitHub <noreply@github.com>2025-11-05 13:51:12 +0000
commit4f0908584b2047d7722d535c596179d3620e9310 (patch)
treece90ab9ff0cd888fa6092ecb852d6934b0bdc877
parentea5db0e04864f7e453a5b5faa305df22543c4b75 (diff)
parentc937d38db2b31b885256b9aac0d606032f6c5343 (diff)
Merge pull request #5890 from odin-lang/bill/all_or_none
`struct #all_or_none`
-rw-r--r--base/runtime/core.odin8
-rw-r--r--base/runtime/print.odin6
-rw-r--r--core/odin/ast/ast.odin1
-rw-r--r--core/odin/doc-format/doc_format.odin1
-rw-r--r--core/odin/parser/parser.odin44
-rw-r--r--core/reflect/types.odin6
-rw-r--r--src/check_expr.cpp45
-rw-r--r--src/check_type.cpp7
-rw-r--r--src/docs_format.cpp62
-rw-r--r--src/docs_writer.cpp7
-rw-r--r--src/llvm_backend_type.cpp8
-rw-r--r--src/name_canonicalization.cpp5
-rw-r--r--src/parser.cpp18
-rw-r--r--src/parser.hpp1
-rw-r--r--src/types.cpp8
15 files changed, 184 insertions, 43 deletions
diff --git a/base/runtime/core.odin b/base/runtime/core.odin
index 2e70147db..58a0b8ad1 100644
--- a/base/runtime/core.odin
+++ b/base/runtime/core.odin
@@ -118,10 +118,10 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
Type_Info_Struct_Flag :: enum u8 {
- packed = 0,
- raw_union = 1,
- _ = 2,
- align = 3,
+ packed = 0,
+ raw_union = 1,
+ all_or_none = 2,
+ align = 3,
}
Type_Info_Struct :: struct {
diff --git a/base/runtime/print.odin b/base/runtime/print.odin
index 2cfb6661b..90119c699 100644
--- a/base/runtime/print.odin
+++ b/base/runtime/print.odin
@@ -408,9 +408,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
}
print_string("struct ")
- if .packed in info.flags { print_string("#packed ") }
- if .raw_union in info.flags { print_string("#raw_union ") }
- // if .no_copy in info.flags { print_string("#no_copy ") }
+ if .packed in info.flags { print_string("#packed ") }
+ if .raw_union in info.flags { print_string("#raw_union ") }
+ if .all_or_none in info.flags { print_string("#all_or_none ") }
if .align in info.flags {
print_string("#align(")
print_u64(u64(ti.align))
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 4dcc1f215..a8c198476 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -790,6 +790,7 @@ Struct_Type :: struct {
is_packed: bool,
is_raw_union: bool,
is_no_copy: bool,
+ is_all_or_none: bool,
fields: ^Field_List,
name_count: int,
}
diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin
index e37092948..a60768931 100644
--- a/core/odin/doc-format/doc_format.odin
+++ b/core/odin/doc-format/doc_format.odin
@@ -281,6 +281,7 @@ Type_Flag_Struct :: enum u32le {
Polymorphic = 0,
Packed = 1,
Raw_Union = 2,
+ All_Or_None = 3,
}
Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le]
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 9ce484a10..ce088fc64 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -2658,11 +2658,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
align: ^ast.Expr
min_field_align: ^ast.Expr
max_field_align: ^ast.Expr
- is_packed: bool
- is_raw_union: bool
- is_no_copy: bool
- fields: ^ast.Field_List
- name_count: int
+ is_packed: bool
+ is_raw_union: bool
+ is_no_copy: bool
+ is_all_or_none: bool
+ fields: ^ast.Field_List
+ name_count: int
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2684,6 +2685,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
}
is_packed = true
+ case "all_or_none":
+ if is_all_or_none {
+ error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+ }
+ is_all_or_none = true
case "align":
if align != nil {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
@@ -2726,6 +2732,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tok.pos, "'#raw_union' cannot also be '#packed")
}
+ if is_raw_union && is_all_or_none {
+ is_all_or_none = false
+ error(p, tok.pos, "'#raw_union' cannot also be '#all_or_none")
+ }
+
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2745,17 +2756,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
- st.poly_params = poly_params
- st.align = align
- st.min_field_align = min_field_align
- st.max_field_align = max_field_align
- st.is_packed = is_packed
- st.is_raw_union = is_raw_union
- st.is_no_copy = is_no_copy
- st.fields = fields
- st.name_count = name_count
- st.where_token = where_token
- st.where_clauses = where_clauses
+ st.poly_params = poly_params
+ st.align = align
+ st.min_field_align = min_field_align
+ st.max_field_align = max_field_align
+ st.is_packed = is_packed
+ st.is_raw_union = is_raw_union
+ st.is_no_copy = is_no_copy
+ st.is_all_or_none = is_all_or_none
+ st.fields = fields
+ st.name_count = name_count
+ st.where_token = where_token
+ st.where_clauses = where_clauses
return st
case .Union:
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 2e82e29b1..f71395298 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -696,9 +696,9 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
}
io.write_string(w, "struct ", &n) or_return
- if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return }
- if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return }
- // if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return }
+ if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return }
+ if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return }
+ if .all_or_none in info.flags { io.write_string(w, "#all_or_none ", &n) or_return }
if .align in info.flags {
io.write_string(w, "#align(", &n) or_return
io.write_i64(w, i64(ti.align), 10, &n) or_return
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 8ac277917..677735c44 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -9838,6 +9838,51 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
c->bit_field_bit_size = prev_bit_field_bit_size;
}
+
+ if (bt->kind == Type_Struct && bt->Struct.is_all_or_none && elems.count > 0 && bt->Struct.fields.count > 0) {
+ PtrSet<Entity *> missing_fields = {};
+ defer (ptr_set_destroy(&missing_fields));
+
+ for_array(i, bt->Struct.fields) {
+ Entity *field = bt->Struct.fields[i];
+ String name = field->token.string;
+ if (is_blank_ident(name) || name == "") {
+ continue;
+ }
+ bool found = string_set_exists(&fields_visited, name);
+ String *raw_union = string_map_get(&fields_visited_through_raw_union, name);
+ if (!found && raw_union == nullptr) {
+ ptr_set_add(&missing_fields, field);
+ }
+ }
+
+ if (missing_fields.count > 0) {
+ ERROR_BLOCK();
+
+ if (build_context.terse_errors) {
+ gbString fields_string = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(fields_string));
+ isize i = 0;
+ FOR_PTR_SET(field, missing_fields) {
+ if (i > 0) {
+ fields_string = gb_string_appendc(fields_string, ", ");
+ }
+ String name = field->token.string;
+ fields_string = gb_string_append_length(fields_string, name.text, name.len);
+ i += 1;
+ }
+
+ error(o->expr, "All or none of the fields must be assigned to a struct with '#all_or_none' applied, missing fields: %s", fields_string);
+ } else {
+ error(o->expr, "All or none of the fields must be assigned to a struct with '#all_or_none' applied, missing fields:");
+ FOR_PTR_SET(field, missing_fields) {
+ gbString s = type_to_string(field->type);
+ error_line("\t%.*s: %s\n", LIT(field->token.string), s);
+ gb_string_free(s);
+ }
+ }
+ }
+ }
}
gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 5accfbd9f..af07efd8f 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -654,9 +654,10 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
context = str_lit("struct #raw_union");
}
- struct_type->Struct.node = node;
- struct_type->Struct.scope = ctx->scope;
- struct_type->Struct.is_packed = st->is_packed;
+ struct_type->Struct.node = node;
+ struct_type->Struct.scope = ctx->scope;
+ struct_type->Struct.is_packed = st->is_packed;
+ struct_type->Struct.is_all_or_none = st->is_all_or_none;
struct_type->Struct.polymorphic_params = check_record_polymorphic_params(
ctx, st->polymorphic_params,
&struct_type->Struct.is_polymorphic,
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 6378971d0..2235789d5 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -94,6 +94,7 @@ enum OdinDocTypeFlag_Struct : u32 {
OdinDocTypeFlag_Struct_polymorphic = 1<<0,
OdinDocTypeFlag_Struct_packed = 1<<1,
OdinDocTypeFlag_Struct_raw_union = 1<<2,
+ OdinDocTypeFlag_Struct_all_or_none = 1<<3,
};
enum OdinDocTypeFlag_Union : u32 {
@@ -124,21 +125,76 @@ enum {
struct OdinDocType {
OdinDocTypeKind kind;
+ // Type_Kind specific used by some types
+ // Underlying flag types:
+ // .Basic - Type_Flags_Basic
+ // .Struct - Type_Flags_Struct
+ // .Union - Type_Flags_Union
+ // .Proc - Type_Flags_Proc
+ // .Bit_Set - Type_Flags_Bit_Set
u32 flags;
+
+ // Used by:
+ // .Basic
+ // .Named
+ // .Generic
OdinDocString name;
+
+ // Used By: .Struct, .Union
OdinDocString custom_align;
- // Used by some types
+ // Used by:
+ // .Array - 1 count: 0=len
+ // .Enumerated_Array - 1 count: 0=len
+ // .SOA_Struct_Fixed - 1 count: 0=len
+ // .Bit_Set - 2 count: 0=lower, 1=upper
+ // .Simd_Vector - 1 count: 0=len
+ // .Matrix - 2 count: 0=row_count, 1=column_count
+ // .Struct - <=2 count: 0=min_field_align, 1=max_field_align
u32 elem_count_len;
i64 elem_counts[OdinDocType_ElemsCap];
- // Each of these is esed by some types, not all
+ // Used by: .Procedures
+ // blank implies the "odin" calling convention
OdinDocString calling_convention;
+
+ // Used by:
+ // .Named - 1 type: 0=base type
+ // .Generic - <1 type: 0=specialization
+ // .Pointer - 1 type: 0=element
+ // .Array - 1 type: 0=element
+ // .Enumerated_Array - 2 types: 0=index and 1=element
+ // .Slice - 1 type: 0=element
+ // .Dynamic_Array - 1 type: 0=element
+ // .Map - 2 types: 0=key, 1=value
+ // .SOA_Struct_Fixed - 1 type: underlying SOA struct element
+ // .SOA_Struct_Slice - 1 type: underlying SOA struct element
+ // .SOA_Struct_Dynamic - 1 type: underlying SOA struct element
+ // .Union - 0+ types: variants
+ // .Enum - <1 type: 0=base type
+ // .Proc - 2 types: 0=parameters, 1=results
+ // .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set)
+ // .Simd_Vector - 1 type: 0=element
+ // .Relative_Pointer - 2 types: 0=pointer type, 1=base integer
+ // .Multi_Pointer - 1 type: 0=element
+ // .Matrix - 1 type: 0=element
+ // .Soa_Pointer - 1 type: 0=element
+ // .Bit_Field - 1 type: 0=backing type
OdinDocArray<OdinDocTypeIndex> types;
+
+ // Used by:
+ // .Named - 1 field for the definition
+ // .Struct - fields
+ // .Enum - fields
+ // .Parameters - parameters (procedures only)
OdinDocArray<OdinDocEntityIndex> entities;
+
+ // Used By: .Struct, .Union
OdinDocTypeIndex polmorphic_params;
+ // Used By: .Struct, .Union
OdinDocArray<OdinDocString> where_clauses;
- OdinDocArray<OdinDocString> tags; // struct field tags
+ // Used By: .Struct
+ OdinDocArray<OdinDocString> tags;
};
struct OdinDocAttribute {
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 1f2325980..3edd7da9d 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -620,6 +620,13 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type, bool ca
if (type->Struct.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Struct_polymorphic; }
if (type->Struct.is_packed) { doc_type.flags |= OdinDocTypeFlag_Struct_packed; }
if (type->Struct.is_raw_union) { doc_type.flags |= OdinDocTypeFlag_Struct_raw_union; }
+ if (type->Struct.is_all_or_none) { doc_type.flags |= OdinDocTypeFlag_Struct_all_or_none; }
+
+ if (type->Struct.custom_min_field_align > 0 || type->Struct.custom_max_field_align > 0) {
+ doc_type.elem_count_len = 2;
+ doc_type.elem_counts[0] = cast(u32)gb_max(type->Struct.custom_min_field_align, 0);
+ doc_type.elem_counts[1] = cast(u32)gb_max(type->Struct.custom_max_field_align, 0);
+ }
auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->Struct.fields.count);
defer (array_free(&fields));
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index 474999191..382304a4e 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -817,10 +817,10 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
{
u8 flags = 0;
- if (t->Struct.is_packed) flags |= 1<<0;
- if (t->Struct.is_raw_union) flags |= 1<<1;
- //
- if (t->Struct.custom_align) flags |= 1<<3;
+ if (t->Struct.is_packed) flags |= 1<<0;
+ if (t->Struct.is_raw_union) flags |= 1<<1;
+ if (t->Struct.is_all_or_none) flags |= 1<<2;
+ if (t->Struct.custom_align) flags |= 1<<3;
vals[6] = lb_const_int(m, t_u8, flags).value;
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp
index 87d7a8522..7cc4ad893 100644
--- a/src/name_canonicalization.cpp
+++ b/src/name_canonicalization.cpp
@@ -749,8 +749,9 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
write_canonical_params(w, type->Struct.polymorphic_params);
}
- if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
- if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
+ if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
+ if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
+ if (type->Struct.is_all_or_none) type_writer_appendc(w, "#all_or_none");
if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
diff --git a/src/parser.cpp b/src/parser.cpp
index 152e55f8b..d3b35f3f4 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1230,7 +1230,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 *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, bool is_all_or_none,
Ast *align, Ast *min_field_align, Ast *max_field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
@@ -1241,6 +1241,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_packed = is_packed;
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_no_copy = is_no_copy;
+ result->StructType.is_all_or_none = is_all_or_none;
result->StructType.align = align;
result->StructType.min_field_align = min_field_align;
result->StructType.max_field_align = max_field_align;
@@ -2773,6 +2774,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
Token token = expect_token(f, Token_struct);
Ast *polymorphic_params = nullptr;
bool is_packed = false;
+ bool is_all_or_none = false;
bool is_raw_union = false;
bool no_copy = false;
Ast *align = nullptr;
@@ -2802,6 +2804,11 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
is_packed = true;
+ } else if (tag.string == "all_or_none") {
+ if (is_packed) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ is_all_or_none = true;
} else if (tag.string == "align") {
if (align) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
@@ -2872,6 +2879,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
is_packed = false;
syntax_error(token, "'#raw_union' cannot also be '#packed'");
}
+ if (is_raw_union && is_all_or_none) {
+ is_all_or_none = false;
+ syntax_error(token, "'#raw_union' cannot also be '#all_or_none'");
+ }
Token where_token = {};
Array<Ast *> where_clauses = {};
@@ -2901,7 +2912,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
parser_check_polymorphic_record_parameters(f, polymorphic_params);
- return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses);
+ return ast_struct_type(f, token, decls, name_count,
+ polymorphic_params, is_packed, is_raw_union, no_copy, is_all_or_none,
+ align, min_field_align, max_field_align,
+ where_token, where_clauses);
} break;
case Token_union: {
diff --git a/src/parser.hpp b/src/parser.hpp
index 6127468d4..71b61d95f 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -766,6 +766,7 @@ AST_KIND(_TypeBegin, "", bool) \
bool is_packed; \
bool is_raw_union; \
bool is_no_copy; \
+ bool is_all_or_none; \
}) \
AST_KIND(UnionType, "union type", struct { \
Scope *scope; \
diff --git a/src/types.cpp b/src/types.cpp
index b9089b9fc..eb20b8edf 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -162,6 +162,7 @@ struct TypeStruct {
bool are_offsets_set : 1;
bool is_packed : 1;
bool is_raw_union : 1;
+ bool is_all_or_none : 1;
bool is_poly_specialized : 1;
std::atomic<bool> are_offsets_being_processed;
@@ -3084,9 +3085,10 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
break;
case Type_Struct:
- if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
- x->Struct.fields.count == y->Struct.fields.count &&
- x->Struct.is_packed == y->Struct.is_packed &&
+ if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
+ x->Struct.fields.count == y->Struct.fields.count &&
+ x->Struct.is_packed == y->Struct.is_packed &&
+ x->Struct.is_all_or_none == y->Struct.is_all_or_none &&
x->Struct.soa_kind == y->Struct.soa_kind &&
x->Struct.soa_count == y->Struct.soa_count &&
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {