Browse Source

union #no_nil

gingerBill 6 years ago
parent
commit
4ab9edeb53
7 changed files with 34 additions and 9 deletions
  1. 7 1
      core/fmt/fmt.odin
  2. 4 3
      core/runtime/core.odin
  3. 1 0
      src/check_type.cpp
  4. 3 0
      src/ir.cpp
  5. 9 2
      src/parser.cpp
  6. 1 0
      src/parser.hpp
  7. 9 3
      src/types.cpp

+ 7 - 1
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;

+ 4 - 3
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,

+ 1 - 0
src/check_type.cpp

@@ -715,6 +715,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 	}
 
 	union_type->Union.variants = variants;
+	union_type->Union.no_nil = ut->no_nil;
 
 	if (ut->align != nullptr) {
 		i64 custom_align = 1;

+ 3 - 0
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;

+ 9 - 2
src/parser.cpp

@@ -911,12 +911,13 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
 }
 
 
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align) {
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> 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<Ast *>(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: {

+ 1 - 0
src/parser.hpp

@@ -480,6 +480,7 @@ AST_KIND(_TypeBegin, "", bool) \
 		Array<Ast *> variants;   \
 		Ast *polymorphic_params; \
 		Ast *        align;      \
+		bool         no_nil;     \
 	}) \
 	AST_KIND(EnumType, "enum type", struct { \
 		Token        token; \

+ 9 - 3
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;