mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
union #shared_nil
This adds a feature to `union` which requires all the variants to have a `nil` value and on assign to the union, checks whether that value is `nil` or not. If the value is `nil`, the union will be `nil` (thus sharing the `nil` value)
This commit is contained in:
@@ -136,6 +136,7 @@ Type_Info_Union :: struct {
|
|||||||
custom_align: bool,
|
custom_align: bool,
|
||||||
no_nil: bool,
|
no_nil: bool,
|
||||||
maybe: bool,
|
maybe: bool,
|
||||||
|
shared_nil: bool,
|
||||||
}
|
}
|
||||||
Type_Info_Enum :: struct {
|
Type_Info_Enum :: struct {
|
||||||
base: ^Type_Info,
|
base: ^Type_Info,
|
||||||
|
|||||||
+5
-2
@@ -10047,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
|
|||||||
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
|
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
|
||||||
str = gb_string_appendc(str, ") ");
|
str = gb_string_appendc(str, ") ");
|
||||||
}
|
}
|
||||||
if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
|
switch (st->kind) {
|
||||||
if (st->maybe) str = gb_string_appendc(str, "#maybe ");
|
case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
|
||||||
|
case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
|
||||||
|
case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
|
||||||
|
}
|
||||||
if (st->align) {
|
if (st->align) {
|
||||||
str = gb_string_appendc(str, "#align ");
|
str = gb_string_appendc(str, "#align ");
|
||||||
str = write_expr_to_string(str, st->align, shorthand);
|
str = write_expr_to_string(str, st->align, shorthand);
|
||||||
|
|||||||
+14
-5
@@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
array_add(&variants, t);
|
array_add(&variants, t);
|
||||||
|
|
||||||
|
if (ut->kind == UnionType_shared_nil) {
|
||||||
|
if (!type_has_nil(t)) {
|
||||||
|
gbString s = type_to_string(t);
|
||||||
|
error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
|
||||||
|
gb_string_free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
union_type->Union.variants = slice_from_array(variants);
|
union_type->Union.variants = slice_from_array(variants);
|
||||||
union_type->Union.no_nil = ut->no_nil;
|
union_type->Union.kind = ut->kind;
|
||||||
union_type->Union.maybe = ut->maybe;
|
switch (ut->kind) {
|
||||||
if (union_type->Union.no_nil) {
|
case UnionType_no_nil:
|
||||||
if (variants.count < 2) {
|
if (variants.count < 2) {
|
||||||
error(ut->align, "A union with #no_nil must have at least 2 variants");
|
error(ut->align, "A union with #no_nil must have at least 2 variants");
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (union_type->Union.maybe) {
|
case UnionType_maybe:
|
||||||
if (variants.count != 1) {
|
if (variants.count != 1) {
|
||||||
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
|
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ut->align != nullptr) {
|
if (ut->align != nullptr) {
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
|
|||||||
OdinDocTypeFlag_Union_polymorphic = 1<<0,
|
OdinDocTypeFlag_Union_polymorphic = 1<<0,
|
||||||
OdinDocTypeFlag_Union_no_nil = 1<<1,
|
OdinDocTypeFlag_Union_no_nil = 1<<1,
|
||||||
OdinDocTypeFlag_Union_maybe = 1<<2,
|
OdinDocTypeFlag_Union_maybe = 1<<2,
|
||||||
|
OdinDocTypeFlag_Union_shared_nil = 1<<3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OdinDocTypeFlag_Proc : u32 {
|
enum OdinDocTypeFlag_Proc : u32 {
|
||||||
|
|||||||
+5
-3
@@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
|
|||||||
case Type_Union:
|
case Type_Union:
|
||||||
doc_type.kind = OdinDocType_Union;
|
doc_type.kind = OdinDocType_Union;
|
||||||
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
|
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
|
||||||
if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
|
switch (type->Union.kind) {
|
||||||
if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
|
case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break;
|
||||||
|
case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
|
||||||
|
case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
|
auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
|
||||||
defer (array_free(&variants));
|
defer (array_free(&variants));
|
||||||
|
|||||||
@@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
|
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
|
||||||
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
|
Type *pt = base_type(type_deref(parent.type));
|
||||||
|
GB_ASSERT(pt->kind == Type_Union);
|
||||||
|
if (pt->Union.kind == UnionType_shared_nil) {
|
||||||
|
lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
|
||||||
|
lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
|
||||||
|
lbBlock *done = lb_create_block(p, "shared_nil.done");
|
||||||
|
|
||||||
lb_emit_store(p, underlying, variant);
|
lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
|
||||||
lb_emit_store_union_variant_tag(p, parent, variant_type);
|
lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
|
||||||
|
|
||||||
|
lb_start_block(p, if_nil);
|
||||||
|
lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
|
||||||
|
lb_emit_jump(p, done);
|
||||||
|
|
||||||
|
lb_start_block(p, if_not_nil);
|
||||||
|
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
|
||||||
|
lb_emit_store(p, underlying, variant);
|
||||||
|
lb_emit_store_union_variant_tag(p, parent, variant_type);
|
||||||
|
lb_emit_jump(p, done);
|
||||||
|
|
||||||
|
lb_start_block(p, done);
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
|
||||||
|
|
||||||
|
lb_emit_store(p, underlying, variant);
|
||||||
|
lb_emit_store_union_variant_tag(p, parent, variant_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
|||||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
|
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
|
||||||
|
|
||||||
{
|
{
|
||||||
LLVMValueRef vals[7] = {};
|
LLVMValueRef vals[8] = {};
|
||||||
|
|
||||||
isize variant_count = gb_max(0, t->Union.variants.count);
|
isize variant_count = gb_max(0, t->Union.variants.count);
|
||||||
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
|
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
|
||||||
@@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
|||||||
}
|
}
|
||||||
|
|
||||||
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
|
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
|
||||||
vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
|
vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
|
||||||
vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
|
vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value;
|
||||||
|
vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
|
||||||
|
|
||||||
for (isize i = 0; i < gb_count_of(vals); i++) {
|
for (isize i = 0; i < gb_count_of(vals); i++) {
|
||||||
if (vals[i] == nullptr) {
|
if (vals[i] == nullptr) {
|
||||||
|
|||||||
+26
-4
@@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
|
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind,
|
||||||
Token where_token, Array<Ast *> const &where_clauses) {
|
Token where_token, Array<Ast *> const &where_clauses) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_UnionType);
|
Ast *result = alloc_ast_node(f, Ast_UnionType);
|
||||||
result->UnionType.token = token;
|
result->UnionType.token = token;
|
||||||
result->UnionType.variants = slice_from_array(variants);
|
result->UnionType.variants = slice_from_array(variants);
|
||||||
result->UnionType.polymorphic_params = polymorphic_params;
|
result->UnionType.polymorphic_params = polymorphic_params;
|
||||||
result->UnionType.align = align;
|
result->UnionType.align = align;
|
||||||
result->UnionType.no_nil = no_nil;
|
result->UnionType.kind = kind;
|
||||||
result->UnionType.maybe = maybe;
|
|
||||||
result->UnionType.where_token = where_token;
|
result->UnionType.where_token = where_token;
|
||||||
result->UnionType.where_clauses = slice_from_array(where_clauses);
|
result->UnionType.where_clauses = slice_from_array(where_clauses);
|
||||||
return result;
|
return result;
|
||||||
@@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
Ast *align = nullptr;
|
Ast *align = nullptr;
|
||||||
bool no_nil = false;
|
bool no_nil = false;
|
||||||
bool maybe = false;
|
bool maybe = false;
|
||||||
|
bool shared_nil = false;
|
||||||
|
|
||||||
|
UnionTypeKind union_kind = UnionType_Normal;
|
||||||
|
|
||||||
Token start_token = f->curr_token;
|
Token start_token = f->curr_token;
|
||||||
|
|
||||||
@@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
||||||
}
|
}
|
||||||
no_nil = true;
|
no_nil = true;
|
||||||
|
} else if (tag.string == "shared_nil") {
|
||||||
|
if (shared_nil) {
|
||||||
|
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
||||||
|
}
|
||||||
|
shared_nil = true;
|
||||||
} else if (tag.string == "maybe") {
|
} else if (tag.string == "maybe") {
|
||||||
if (maybe) {
|
if (maybe) {
|
||||||
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
||||||
@@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
if (no_nil && maybe) {
|
if (no_nil && maybe) {
|
||||||
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
|
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
|
||||||
}
|
}
|
||||||
|
if (no_nil && shared_nil) {
|
||||||
|
syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together");
|
||||||
|
}
|
||||||
|
if (shared_nil && maybe) {
|
||||||
|
syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (maybe) {
|
||||||
|
union_kind = UnionType_maybe;
|
||||||
|
} else if (no_nil) {
|
||||||
|
union_kind = UnionType_no_nil;
|
||||||
|
} else if (shared_nil) {
|
||||||
|
union_kind = UnionType_shared_nil;
|
||||||
|
}
|
||||||
|
|
||||||
skip_possible_newline_for_literal(f);
|
skip_possible_newline_for_literal(f);
|
||||||
|
|
||||||
@@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
|
|
||||||
Token close = expect_closing_brace_of_field_list(f);
|
Token close = expect_closing_brace_of_field_list(f);
|
||||||
|
|
||||||
return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
|
return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Token_enum: {
|
case Token_enum: {
|
||||||
|
|||||||
+8
-2
@@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
|
|||||||
"intel",
|
"intel",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum UnionTypeKind : u8 {
|
||||||
|
UnionType_Normal = 0,
|
||||||
|
UnionType_maybe = 1,
|
||||||
|
UnionType_no_nil = 2,
|
||||||
|
UnionType_shared_nil = 3,
|
||||||
|
};
|
||||||
|
|
||||||
#define AST_KINDS \
|
#define AST_KINDS \
|
||||||
AST_KIND(Ident, "identifier", struct { \
|
AST_KIND(Ident, "identifier", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \
|
|||||||
Slice<Ast *> variants; \
|
Slice<Ast *> variants; \
|
||||||
Ast *polymorphic_params; \
|
Ast *polymorphic_params; \
|
||||||
Ast * align; \
|
Ast * align; \
|
||||||
bool maybe; \
|
UnionTypeKind kind; \
|
||||||
bool no_nil; \
|
|
||||||
Token where_token; \
|
Token where_token; \
|
||||||
Slice<Ast *> where_clauses; \
|
Slice<Ast *> where_clauses; \
|
||||||
}) \
|
}) \
|
||||||
|
|||||||
+12
-10
@@ -165,9 +165,8 @@ struct TypeUnion {
|
|||||||
|
|
||||||
i16 tag_size;
|
i16 tag_size;
|
||||||
bool is_polymorphic;
|
bool is_polymorphic;
|
||||||
bool is_poly_specialized : 1;
|
bool is_poly_specialized;
|
||||||
bool no_nil : 1;
|
UnionTypeKind kind;
|
||||||
bool maybe : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeProc {
|
struct TypeProc {
|
||||||
@@ -1664,7 +1663,7 @@ bool is_type_map(Type *t) {
|
|||||||
|
|
||||||
bool is_type_union_maybe_pointer(Type *t) {
|
bool is_type_union_maybe_pointer(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
if (t->kind == Type_Union && t->Union.maybe) {
|
if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
|
||||||
if (t->Union.variants.count == 1) {
|
if (t->Union.variants.count == 1) {
|
||||||
Type *v = t->Union.variants[0];
|
Type *v = t->Union.variants[0];
|
||||||
return is_type_pointer(v) || is_type_multi_pointer(v);
|
return is_type_pointer(v) || is_type_multi_pointer(v);
|
||||||
@@ -1676,7 +1675,7 @@ bool is_type_union_maybe_pointer(Type *t) {
|
|||||||
|
|
||||||
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
|
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
if (t->kind == Type_Union && t->Union.maybe) {
|
if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
|
||||||
if (t->Union.variants.count == 1) {
|
if (t->Union.variants.count == 1) {
|
||||||
Type *v = t->Union.variants[0];
|
Type *v = t->Union.variants[0];
|
||||||
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
|
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
|
||||||
@@ -2168,7 +2167,7 @@ bool type_has_nil(Type *t) {
|
|||||||
case Type_Map:
|
case Type_Map:
|
||||||
return true;
|
return true;
|
||||||
case Type_Union:
|
case Type_Union:
|
||||||
return !t->Union.no_nil;
|
return t->Union.kind != UnionType_no_nil;
|
||||||
case Type_Struct:
|
case Type_Struct:
|
||||||
if (is_type_soa_struct(t)) {
|
if (is_type_soa_struct(t)) {
|
||||||
switch (t->Struct.soa_kind) {
|
switch (t->Struct.soa_kind) {
|
||||||
@@ -2454,7 +2453,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
|
|||||||
if (y->kind == Type_Union) {
|
if (y->kind == Type_Union) {
|
||||||
if (x->Union.variants.count == y->Union.variants.count &&
|
if (x->Union.variants.count == y->Union.variants.count &&
|
||||||
x->Union.custom_align == y->Union.custom_align &&
|
x->Union.custom_align == y->Union.custom_align &&
|
||||||
x->Union.no_nil == y->Union.no_nil) {
|
x->Union.kind == y->Union.kind) {
|
||||||
// NOTE(bill): zeroth variant is nullptr
|
// NOTE(bill): zeroth variant is nullptr
|
||||||
for_array(i, x->Union.variants) {
|
for_array(i, x->Union.variants) {
|
||||||
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
|
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
|
||||||
@@ -2598,7 +2597,7 @@ i64 union_variant_index(Type *u, Type *v) {
|
|||||||
for_array(i, u->Union.variants) {
|
for_array(i, u->Union.variants) {
|
||||||
Type *vt = u->Union.variants[i];
|
Type *vt = u->Union.variants[i];
|
||||||
if (are_types_identical(v, vt)) {
|
if (are_types_identical(v, vt)) {
|
||||||
if (u->Union.no_nil) {
|
if (u->Union.kind == UnionType_no_nil) {
|
||||||
return cast(i64)(i+0);
|
return cast(i64)(i+0);
|
||||||
} else {
|
} else {
|
||||||
return cast(i64)(i+1);
|
return cast(i64)(i+1);
|
||||||
@@ -4021,8 +4020,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
|
|||||||
|
|
||||||
case Type_Union:
|
case Type_Union:
|
||||||
str = gb_string_appendc(str, "union");
|
str = gb_string_appendc(str, "union");
|
||||||
if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil");
|
switch (type->Union.kind) {
|
||||||
if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe");
|
case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break;
|
||||||
|
case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
|
||||||
|
case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
|
||||||
|
}
|
||||||
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
|
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
|
||||||
str = gb_string_appendc(str, " {");
|
str = gb_string_appendc(str, " {");
|
||||||
for_array(i, type->Union.variants) {
|
for_array(i, type->Union.variants) {
|
||||||
|
|||||||
Reference in New Issue
Block a user