Browse Source

Change union layout to store type info rather than an integer; ternary expression for types with constant condition

Ginger Bill 8 years ago
parent
commit
6113164211
9 changed files with 222 additions and 85 deletions
  1. 1 1
      code/demo.odin
  2. 2 1
      core/_preload.odin
  3. 41 0
      core/c.odin
  4. 9 1
      core/fmt.odin
  5. 129 20
      src/check_expr.cpp
  6. 26 56
      src/ir.cpp
  7. 2 1
      src/ir_print.cpp
  8. 1 1
      src/parser.cpp
  9. 11 4
      src/types.cpp

+ 1 - 1
code/demo.odin

@@ -1,5 +1,5 @@
 import "fmt.odin";
 
 main :: proc() {
-	fmt.println("Hellope, World!");
+	fmt.println("Hellope!");
 }

+ 2 - 1
core/_preload.odin

@@ -82,7 +82,8 @@ TypeInfo :: struct #ordered {
 	Struct       :: Record;
 	RawUnion     :: Record;
 	Union :: struct #ordered {
-		variants: []^TypeInfo;
+		variants:   []^TypeInfo;
+		tag_offset: int;
 	};
 	Enum :: struct #ordered {
 		base:   ^TypeInfo;

+ 41 - 0
core/c.odin

@@ -0,0 +1,41 @@
+CHAR_BIT :: 8;
+
+c_bool           :: bool;
+
+c_char           :: u8;
+
+c_schar          :: i8;
+c_uchar          :: i8;
+
+c_short          :: i16;
+c_ushort         :: i16;
+
+c_int            :: i32;
+c_uint           :: u32;
+
+c_long  :: ODIN_OS == "windows" ?
+	i32 :
+	(size_of(int) == 4) ?
+		i32 :
+		i64;
+
+c_ulong :: ODIN_OS == "windows" ?
+	u32 :
+	(size_of(int) == 4) ?
+		u32 :
+		u64;
+
+c_longlong       :: i64;
+c_ulonglong      :: u64;
+
+c_float          :: f32;
+c_double         :: f64;
+
+c_complex_float  :: complex64;
+c_complex_double :: complex128;
+
+c_size_t         :: uint;
+c_ssize_t        :: int;
+c_ptrdiff_t      :: int;
+c_uintptr_t      :: uint;
+c_intptr_t       :: int;

+ 9 - 1
core/fmt.odin

@@ -866,7 +866,15 @@ fmt_value :: proc(fi: ^FmtInfo, v: any, verb: rune) {
 		}
 
 	case Union:
-		write_string(fi.buf, "(union)");
+		data := cast(^u8)v.data;
+		tipp := cast(^^TypeInfo)(data + info.tag_offset);
+		if data == nil || tipp == nil {
+			write_string(fi.buf, "(union)");
+		} else {
+			ti := tipp^;
+			fmt_arg(fi, any{data, ti}, verb);
+		}
+
 
 	case RawUnion:
 		write_string(fi.buf, "(raw_union)");

+ 129 - 20
src/check_expr.cpp

@@ -1326,10 +1326,6 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 	array_add(&variants, t_invalid);
 
 	union_type->Union.scope               = c->context.scope;
-	{
-		Entity *__tag = make_entity_field(c->allocator, nullptr, make_token_ident(str_lit("__tag")), t_int, false, -1);
-		union_type->Union.union__tag = __tag;
-	}
 
 	for_array(i, ut->variants) {
 		AstNode *node = ut->variants[i];
@@ -1353,7 +1349,10 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 					}
 				}
 			}
-			if (ok) array_add(&variants, t);
+			if (ok) {
+				add_type_info_type(c, t);
+				array_add(&variants, t);
+			}
 		}
 	}
 
@@ -2044,8 +2043,10 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 						success = false;
 						type = t_invalid;
 					}
-					if (is_type_polymorphic_struct(type)) {
-						error(o.expr, "Cannot pass polymorphic struct as a parameter");
+					if (is_type_polymorphic(type)) {
+						gbString str = type_to_string(type);
+						error(o.expr, "Cannot pass polymorphic type as a parameter, got `%s`", str);
+						gb_string_free(str);
 						success = false;
 						type = t_invalid;
 					}
@@ -3079,6 +3080,15 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 			return true;
 		}
 	case_end;
+
+	case_ast_node(te, TernaryExpr, e);
+		Operand o = {};
+		check_expr_or_type(c, &o, e);
+		if (o.mode == Addressing_Type) {
+			*type = o.type;
+			return true;
+		}
+	case_end;
 	}
 
 	*type = t_invalid;
@@ -4290,6 +4300,90 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
 		}
 	} break;
 
+	case Type_Union:
+	{
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+		i32 count = t->Union.variant_count;
+		i64 *scores = gb_alloc_array(c->tmp_allocator, i64, count);
+		i32 success_count = 0;
+		i32 first_success_index = -1;
+		for (i32 i = 1; i < count; i++) {
+			Type *vt = t->Union.variants[i];
+			i64 score = 0;
+			if (check_is_assignable_to_with_score(c, operand, vt, &score)) {
+				scores[i] = score;
+				success_count += 1;
+				if (first_success_index < 0) {
+					first_success_index = i;
+				}
+			}
+		}
+
+		gbString type_str = type_to_string(target_type);
+		defer (gb_string_free(type_str));
+
+		if (success_count == 1) {
+			operand->mode = Addressing_Value;
+			operand->type = t->Union.variants[first_success_index];
+			target_type = t->Union.variants[first_success_index];
+			break;
+		} else if (success_count > 1) {
+			GB_ASSERT(first_success_index >= 0);
+			operand->mode = Addressing_Invalid;
+			convert_untyped_error(c, operand, target_type);
+
+			gb_printf_err("Ambiguous type conversion to `%s`, which variant did you mean:\n\t", type_str);
+			i32 j = 0;
+			for (i32 i = first_success_index; i < count; i++) {
+				if (scores[i] == 0) continue;
+				if (j > 0 && success_count > 2) gb_printf_err(", ");
+				if (j == success_count-1) {
+					if (success_count == 2) {
+						gb_printf_err(" or ");
+					} else {
+						gb_printf_err(" or ");
+					}
+				}
+				gbString str = type_to_string(t->Union.variants[i]);
+				gb_printf_err("`%s`", str);
+				gb_string_free(str);
+				j++;
+			}
+			gb_printf_err("\n\n");
+
+			return;
+		} else if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
+			target_type = t_untyped_undef;
+		} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
+			operand->mode = Addressing_Invalid;
+			convert_untyped_error(c, operand, target_type);
+			if (count > 1) {
+				gb_printf_err("`%s` is a union which only excepts the following types:\n", type_str);
+				gb_printf_err("\t");
+				for (i32 i = 1; i < count; i++) {
+					Type *v = t->Union.variants[i];
+					if (i > 1 && count > 3) gb_printf_err(", ");
+					if (i == count-1) {
+						if (count == 3) {
+							gb_printf_err(" or ");
+						} else {
+							gb_printf_err("or ");
+						}
+					}
+					gbString str = type_to_string(v);
+					gb_printf_err("`%s`", str);
+					gb_string_free(str);
+				}
+				gb_printf_err("\n\n");
+
+			}
+			return;
+		}
+
+	}
+	/* fallthrough */
+
 
 	default:
 		if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
@@ -6894,16 +6988,6 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 }
 
 
-ExprKind Ov(Checker *c, Operand *operand, AstNode *call) {
-	GB_ASSERT(call->kind == AstNode_MacroCallExpr);
-	ast_node(mce, MacroCallExpr, call);
-
-	error(call, "Macro call expressions are not yet supported");
-	operand->mode = Addressing_Invalid;
-	operand->expr = call;
-	return Expr_Stmt;
-}
-
 void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) {
 	check_expr_base(c, o, e, t);
 	check_not_tuple(c, o);
@@ -7139,10 +7223,10 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 
 		Operand x = {Addressing_Invalid};
 		Operand y = {Addressing_Invalid};
-		check_expr_with_type_hint(c, &x, te->x, type_hint);
+		check_expr_or_type(c, &x, te->x, type_hint);
 
 		if (te->y != nullptr) {
-			check_expr_with_type_hint(c, &y, te->y, type_hint);
+			check_expr_or_type(c, &y, te->y, type_hint);
 		} else {
 			error(node, "A ternary expression must have an else clause");
 			return kind;
@@ -7153,6 +7237,19 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 			return kind;
 		}
 
+		if (x.mode == Addressing_Type && y.mode == Addressing_Type &&
+		    cond.mode == Addressing_Constant && is_type_boolean(cond.type)) {
+			o->mode = Addressing_Type;
+			if (cond.value.value_bool) {
+				o->type = x.type;
+				o->expr = x.expr;
+			} else {
+				o->type = y.type;
+				o->expr = y.expr;
+			}
+			return Expr_Expr;
+		}
+
 		convert_to_typed(c, &x, y.type, 0);
 		if (x.mode == Addressing_Invalid) {
 			return kind;
@@ -7218,7 +7315,18 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 			return kind;
 		}
 
+
 		Type *t = base_type(type);
+		if (is_type_polymorphic(t)) {
+			gbString str = type_to_string(type);
+			error(node, "Cannot use a polymorphic type for a compound literal, got `%s`", str);
+			o->expr = node;
+			o->type = type;
+			gb_string_free(str);
+			return kind;
+		}
+
+
 		switch (t->kind) {
 		case Type_Record: {
 			if (is_type_union(t)) {
@@ -7909,7 +8017,8 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 	case_end;
 
 	case_ast_node(ce, MacroCallExpr, node);
-		return Ov(c, o, node);
+		error(node, "Macro calls are not yet supported");
+		return kind;
 	case_end;
 
 	case_ast_node(de, DerefExpr, node);

+ 26 - 56
src/ir.cpp

@@ -954,7 +954,7 @@ irValue *ir_instr_union_tag_ptr(irProcedure *p, irValue *address) {
 	irValue *v = ir_alloc_instr(p, irInstr_UnionTagPtr);
 	irInstr *i = &v->Instr;
 	i->UnionTagPtr.address = address;
-	i->UnionTagPtr.type = t_int_ptr;
+	i->UnionTagPtr.type = make_type_pointer(p->module->allocator, t_type_info_ptr);
 	return v;
 }
 
@@ -962,7 +962,7 @@ irValue *ir_instr_union_tag_value(irProcedure *p, irValue *address) {
 	irValue *v = ir_alloc_instr(p, irInstr_UnionTagValue);
 	irInstr *i = &v->Instr;
 	i->UnionTagValue.address = address;
-	i->UnionTagValue.type = t_int;
+	i->UnionTagValue.type = t_type_info_ptr;
 	return v;
 }
 
@@ -2218,7 +2218,7 @@ irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) {
 	Type *tpt = ir_type(tag_ptr);
 	GB_ASSERT(is_type_pointer(tpt));
 	tpt = base_type(type_deref(tpt));
-	GB_ASSERT(tpt == t_int);
+	GB_ASSERT(tpt == t_type_info_ptr);
 	return tag_ptr;
 }
 
@@ -2379,15 +2379,8 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) {
 		GB_ASSERT_MSG(gb_is_between(index, 0, t->Record.field_count-1), "0..%d..%d", index, t->Record.field_count);
 		result_type = make_type_pointer(a, t->Record.fields[index]->type);
 	} else if (is_type_union(t)) {
-		type_set_offsets(a, t);
-		GB_ASSERT(t->Record.field_count > 0);
-		if (index == -1) {
-			index = t->Record.field_count+1;
-			result_type = t_int_ptr;
-		} else {
-			GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-			result_type = make_type_pointer(a, t->Record.fields[index]->type);
-		}
+		GB_ASSERT(index == -1);
+		return ir_emit_union_tag_ptr(proc, s);
 	} else if (is_type_tuple(t)) {
 		GB_ASSERT(t->Tuple.variable_count > 0);
 		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
@@ -2449,14 +2442,8 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
 		result_type = t->Record.fields[index]->type;
 	} else if (is_type_union(t)) {
-		type_set_offsets(a, t);
-		if (index == -1) {
-			index = t->Record.field_count+1;
-			result_type = t_int_ptr;
-		} else {
-			GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-		}
-		result_type = t->Record.fields[index]->type;
+		GB_ASSERT(index == -1);
+		return ir_emit_union_tag_value(proc, s);
 	} else if (is_type_tuple(t)) {
 		GB_ASSERT(t->Tuple.variable_count > 0);
 		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
@@ -2523,12 +2510,12 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) {
 		if (is_type_raw_union(type)) {
 			type = type->Record.fields[index]->type;
 			e = ir_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
+		} else if (type->kind == Type_Union) {
+			GB_ASSERT(index == -1);
+			type = t_type_info_ptr;
+			e = ir_emit_struct_ep(proc, e, index);
 		} else if (type->kind == Type_Record) {
-			if (index == -1) {
-				type = t_int;
-			} else {
-				type = type->Record.fields[index]->type;
-			}
+			type = type->Record.fields[index]->type;
 			e = ir_emit_struct_ep(proc, e, index);
 		} else if (type->kind == Type_Tuple) {
 			type = type->Tuple.variables[index]->type;
@@ -2983,7 +2970,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 				ir_emit_store(proc, underlying, value);
 
 				irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
-				ir_emit_store(proc, tag_ptr, ir_const_int(a, i));
+				ir_emit_store(proc, tag_ptr, ir_type_info(proc, vt));
 
 				return ir_emit_load(proc, parent);
 			}
@@ -3308,20 +3295,11 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 	Type *src = base_type(type_deref(src_type));
 	GB_ASSERT(is_type_union(src));
 	Type *dst = tuple->Tuple.variables[0]->type;
-	Type *dst_ptr = make_type_pointer(a, dst);
 
 	irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
 
 	irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
-	irValue *dst_tag = nullptr;
-	for (isize i = 1; i < src->Union.variant_count; i++) {
-		Type *vt = src->Union.variants[i];
-		if (are_types_identical(vt, dst)) {
-			dst_tag = ir_const_int(a, i);
-			break;
-		}
-	}
-	GB_ASSERT(dst_tag != nullptr);
+	irValue *dst_tag = ir_type_info(proc, dst);
 
 	irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
 	irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
@@ -6943,18 +6921,18 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 				irValue *cond = nullptr;
 				if (match_type_kind == MatchType_Union) {
 					Type *bt = type_deref(case_type);
-					irValue *index = nullptr;
+					irValue *variant_tag = nullptr;
 					Type *ut = base_type(type_deref(parent_type));
 					GB_ASSERT(ut->kind == Type_Union);
 					for (isize variant_index = 1; variant_index < ut->Union.variant_count; variant_index++) {
 						Type *vt = ut->Union.variants[variant_index];
 						if (are_types_identical(vt, bt)) {
-							index = ir_const_int(allocator, variant_index);
+							variant_tag = ir_type_info(proc, vt);
 							break;
 						}
 					}
-					GB_ASSERT(index != nullptr);
-					cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index);
+					GB_ASSERT(variant_tag != nullptr);
+					cond = ir_emit_comp(proc, Token_CmpEq, tag_index, variant_tag);
 				} else if (match_type_kind == MatchType_Any) {
 					irValue *any_ti  = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 1));
 					irValue *case_ti = ir_type_info(proc, case_type);
@@ -8166,7 +8144,8 @@ void ir_gen_tree(irGen *s) {
 					tag = ir_emit_conv(proc, variant_ptr, t_type_info_union_ptr);
 
 					{
-						irValue *variant_types = ir_emit_struct_ep(proc, tag, 0);
+						irValue *variant_types  = ir_emit_struct_ep(proc, tag, 0);
+						irValue *tag_offset_ptr = ir_emit_struct_ep(proc, tag, 1);
 
 						isize variant_count = gb_max(0, t->Union.variant_count-1);
 						irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count);
@@ -8183,6 +8162,9 @@ void ir_gen_tree(irGen *s) {
 
 						irValue *count = ir_const_int(a, variant_count);
 						ir_fill_slice(proc, variant_types, memory_types, count, count);
+
+						i64 tag_offset = align_formula(t->Union.variant_block_size, build_context.word_size);
+						ir_emit_store(proc, tag_offset_ptr, ir_const_int(a, tag_offset));
 					}
 
 				} break;
@@ -8323,21 +8305,9 @@ void ir_gen_tree(irGen *s) {
 				if (tag != nullptr) {
 					Type *tag_type = type_deref(ir_type(tag));
 					GB_ASSERT(is_type_named(tag_type));
-					Type *ti = base_type(t_type_info);
-					Type *tiv = base_type(ti->Record.fields_in_src_order[2]->type);
-					GB_ASSERT(is_type_union(tiv));
-					bool found = false;
-					for (isize i = 1; i < tiv->Union.variant_count; i++) {
-						Type *vt = tiv->Union.variants[i];
-						if (are_types_identical(vt, tag_type)) {
-							found = true;
-							irValue *tag_val = ir_const_int(a, i);
-							irValue *ptr = ir_emit_union_tag_ptr(proc, variant_ptr);
-							ir_emit_store(proc, ptr, tag_val);
-							break;
-						}
-					}
-					GB_ASSERT_MSG(found, "Tag type not found: %s", type_to_string(tag_type));
+					irValue *ti = ir_type_info(proc, tag_type);
+					irValue *ptr = ir_emit_union_tag_ptr(proc, variant_ptr);
+					ir_emit_store(proc, ptr, ti);
 				} else {
 					GB_PANIC("Unhandled TypeInfo type: %s", type_to_string(t));
 				}

+ 2 - 1
src/ir_print.cpp

@@ -292,7 +292,8 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 
 		ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
 		ir_fprintf(f, "[%lld x i8], ", block_size);
-		ir_fprintf(f, "i%lld}", word_bits);
+		ir_print_type(f, m, t_type_info_ptr);
+		ir_fprintf(f, "}");
 	#else
 		i64 block_size = total_size - build_context.word_size;
 		ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align, block_size, word_bits);

+ 1 - 1
src/parser.cpp

@@ -4942,7 +4942,7 @@ ParseFileError parse_files(Parser *p, String init_filename) {
 	GB_ASSERT(init_filename.text[init_filename.len] == 0);
 
 	char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char *)&init_filename[0]);
-	String init_fullpath = make_string_c(fullpath_str);
+	String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str));
 	TokenPos init_pos = {};
 	ImportedFile init_imported_file = {init_fullpath, init_fullpath, init_pos};
 

+ 11 - 4
src/types.cpp

@@ -123,7 +123,7 @@ struct TypeRecord {
 		i32      variant_count;                           \
 		AstNode *node;                                    \
 		Scope *  scope;                                   \
-		Entity * union__tag;                              \
+		Entity * union__type_info;                        \
 		i64      variant_block_size;                      \
 		i64      custom_align;                            \
 	})                                                    \
@@ -1596,11 +1596,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 		}
 
 	} else if (type->kind == Type_Union) {
-		if (field_name == "__tag") {
-			Entity *e = type->Union.union__tag;
+		if (field_name == "__type_info") {
+			Entity *e = type->Union.union__type_info;
+			if (e == nullptr) {
+				Entity *__type_info = make_entity_field(a, nullptr, make_token_ident(str_lit("__type_info")), t_type_info_ptr, false, -1);
+				type->Union.union__type_info = __type_info;
+				e = __type_info;
+			}
+
 			GB_ASSERT(e != nullptr);
 			selection_add_index(&sel, -1); // HACK(bill): Leaky memory
 			sel.entity = e;
+
 			return sel;
 		}
 	} else if (type->kind == Type_Record) {
@@ -2067,9 +2074,9 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 		}
 
 		i64 max = 0;
+		i64 field_size = 0;
 		isize variant_count = t->Union.variant_count;
 
-		i64 field_size = max;
 
 		for (isize i = 1; i < variant_count; i++) {
 			Type *variant_type = t->Union.variants[i];