From 4ab9edeb53559f6ab6ef39a5ad9b45564d22aa8e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 9 Jul 2019 10:28:13 +0100 Subject: [PATCH] union #no_nil --- core/fmt/fmt.odin | 8 +++++++- core/runtime/core.odin | 7 ++++--- src/check_type.cpp | 1 + src/ir.cpp | 3 +++ src/parser.cpp | 11 +++++++++-- src/parser.hpp | 1 + src/types.cpp | 12 +++++++++--- 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 7e18b3e4a..1080f9d3d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1326,8 +1326,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case i64: tag = i64(i); case: panic("Invalid union tag type"); } + assert(tag >= 0); - if v.data == nil || tag == 0 { + if v.data == nil { + strings.write_string(fi.buf, "nil"); + } else if info.no_nil { + id := info.variants[tag].id; + fmt_arg(fi, any{v.data, id}, verb); + } else if tag == 0 { strings.write_string(fi.buf, "nil"); } else { id := info.variants[tag-1].id; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 50a221a8c..9ebdc17fa 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -86,10 +86,11 @@ Type_Info_Struct :: struct { custom_align: bool, }; Type_Info_Union :: struct { - variants: []^Type_Info, - tag_offset: uintptr, - tag_type: ^Type_Info, + variants: []^Type_Info, + tag_offset: uintptr, + tag_type: ^Type_Info, custom_align: bool, + no_nil: bool, }; Type_Info_Enum :: struct { base: ^Type_Info, diff --git a/src/check_type.cpp b/src/check_type.cpp index 555b54e78..6cd982bcb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -715,6 +715,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayUnion.variants = variants; + union_type->Union.no_nil = ut->no_nil; if (ut->align != nullptr) { i64 custom_align = 1; diff --git a/src/ir.cpp b/src/ir.cpp index 30ad9d0fa..481114d80 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9991,6 +9991,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *tag_offset_ptr = ir_emit_struct_ep(proc, tag, 1); irValue *tag_type_ptr = ir_emit_struct_ep(proc, tag, 2); irValue *custom_align_ptr = ir_emit_struct_ep(proc, tag, 3); + irValue *no_nil_ptr = ir_emit_struct_ep(proc, tag, 4); isize variant_count = gb_max(0, t->Union.variants.count); irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count); @@ -10018,6 +10019,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *is_custom_align = ir_const_bool(t->Union.custom_align != 0); ir_emit_store(proc, custom_align_ptr, is_custom_align); + + ir_emit_store(proc, no_nil_ptr, ir_const_bool(t->Union.no_nil)); } break; diff --git a/src/parser.cpp b/src/parser.cpp index ff6a03fb6..e262b3196 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -911,12 +911,13 @@ Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align) { +Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = variants; result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; + result->UnionType.no_nil = no_nil; return result; } @@ -1971,6 +1972,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { auto variants = array_make(heap_allocator()); Ast *polymorphic_params = nullptr; Ast *align = nullptr; + bool no_nil = false; CommentGroup *docs = f->lead_comment; Token start_token = f->curr_token; @@ -1993,6 +1995,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); } align = parse_expr(f, true); + } else if (tag.string == "no_nil") { + if (no_nil) { + syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); + } + no_nil = true; } else { syntax_error(tag, "Invalid union tag '#%.*s'", LIT(tag.string)); } @@ -2013,7 +2020,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_token(f, Token_CloseBrace); - return ast_union_type(f, token, variants, polymorphic_params, align); + return ast_union_type(f, token, variants, polymorphic_params, align, no_nil); } break; case Token_enum: { diff --git a/src/parser.hpp b/src/parser.hpp index e08648eca..a6c9d190b 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -480,6 +480,7 @@ AST_KIND(_TypeBegin, "", bool) \ Array variants; \ Ast *polymorphic_params; \ Ast * align; \ + bool no_nil; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ diff --git a/src/types.cpp b/src/types.cpp index 875519f2d..dc7ecb946 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -131,6 +131,7 @@ struct TypeUnion { i64 variant_block_size; i64 custom_align; i64 tag_size; + bool no_nil; bool is_polymorphic; bool is_poly_specialized; @@ -1468,7 +1469,7 @@ bool type_has_nil(Type *t) { case Type_Map: return true; case Type_Union: - return true; + return !t->Union.no_nil; case Type_Struct: return false; case Type_Opaque: @@ -1625,7 +1626,8 @@ bool are_types_identical(Type *x, Type *y) { case Type_Union: if (y->kind == Type_Union) { 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) { // NOTE(bill): zeroth variant is nullptr for_array(i, x->Union.variants) { if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) { @@ -1778,7 +1780,11 @@ i64 union_variant_index(Type *u, Type *v) { for_array(i, u->Union.variants) { Type *vt = u->Union.variants[i]; if (are_types_identical(v, vt)) { - return cast(i64)(i+1); + if (u->Union.no_nil) { + return cast(i64)(i+0); + } else { + return cast(i64)(i+1); + } } } return 0;