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

This commit is contained in:
Ginger Bill
2017-07-19 12:15:21 +01:00
parent 4db462a703
commit 6113164211
9 changed files with 222 additions and 85 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import "fmt.odin";
main :: proc() {
fmt.println("Hellope, World!");
fmt.println("Hellope!");
}
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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];