From 7f884ed25187416bb3994e498eae30fe65233940 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sat, 3 Sep 2016 18:18:45 +0100 Subject: [PATCH] Start implementing Tagged Unions --- examples/demo.odin | 62 ++--------- examples/game.odin | 6 -- src/checker/expr.cpp | 203 +++++++++++++++++++++++++++---------- src/checker/type.cpp | 90 +++++++++++++--- src/codegen/print_llvm.cpp | 10 +- src/codegen/ssa.cpp | 29 ++++++ src/parser.cpp | 41 ++++++-- 7 files changed, 302 insertions(+), 139 deletions(-) diff --git a/examples/demo.odin b/examples/demo.odin index a780bef1d..969b958fc 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -3,62 +3,20 @@ #load "game.odin" main :: proc() { - - print_ints :: proc(args: ..int) { - for i := 0; i < len(args); i++ { - print_int(args[i]) - nl() + Entity :: type union { + FROG: struct { + jump_height: f32 + } + HELICOPTER: struct { + weight: f32 + blade_code: int } } - // print_ints() - // print_ints(1) - print_ints(1, 2, 3, 4, 5) - // print_int(min(1, 2)); nl() - // print_int(max(1, 2)); nl() - // print_int(abs(-1337)); nl() + e: Entity + f: Entity = Entity.FROG{1}; + h: Entity = Entity.HELICOPTER{123, 4}; - // a, b, c := 1, 2, -1337 - - // print_int(min(a, b)); nl() - // print_int(max(a, b)); nl() - // print_int(abs(c) as int); nl() - - // nl() -/* - Vec3 :: type struct { x, y, z: f32 } - Entity :: type struct { - using pos: Vec3 - name: string - } - - Amp :: type struct { - using entity: Entity - jump_height: f32 - } - Frog :: type struct { - using amp: Amp - volume: f64 - } - - f := Frog{}; - f.name = "ribbit" - f.jump_height = 1337 - - e := ^f.entity - parent := e down_cast ^Frog - - print_name :: proc(using e: Entity, v: Vec3) { - print_string(name); nl() - print_int(v.x as int); nl() - } - - print_f32(f.jump_height); nl() - print_f32(parent.jump_height); nl() - - print_name(f, Vec3{1, 2, 3}) - print_name(parent, Vec3{3, 2, 1}) -*/ } nl :: proc() { print_nl() } diff --git a/examples/game.odin b/examples/game.odin index 4420ce448..1568ebabe 100644 --- a/examples/game.odin +++ b/examples/game.odin @@ -122,12 +122,6 @@ display_window :: proc(w: ^Window) { } -Entity :: type struct { - pos: Vec2 - dim: Vec2 -} - - run_game :: proc() { win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline { if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT { diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index dd49e5452..3949aa445 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -53,12 +53,16 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu Type *s = operand->type; - if (are_types_identical(s, type)) + + + if (are_types_identical(s, type)) { return true; + } Type *src = get_base_type(s); Type *dst = get_base_type(type); + if (is_type_untyped(src)) { switch (dst->kind) { case Type_Basic: @@ -72,8 +76,12 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu } } - if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) + if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) { + if (is_type_enum(dst) && is_type_enum(src)) { + return are_types_identical(s, type); + } return true; + } if (is_type_pointer(dst) && is_type_rawptr(src)) return true; @@ -93,6 +101,15 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu } } + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, s)) { + return true; + } + } + } + if (is_argument) { // NOTE(bill): Polymorphism for subtyping if (check_is_assignable_to_using_subtype(type, src)) { @@ -270,64 +287,87 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, } - isize field_index = 0; - for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) { - if (decl->kind != AstNode_VarDecl) - continue; - ast_node(vd, VarDecl, decl); - if (vd->kind != Declaration_Mutable) - continue; - Type *type = check_type(c, vd->type, NULL, cycle_checker); - - if (vd->is_using) { - if (vd->name_count > 1) { - error(&c->error_collector, ast_node_token(vd->name_list), - "Cannot apply `using` to more than one of the same type"); - } - } - - for (AstNode *name = vd->name_list; name != NULL; name = name->next) { - Token name_token = name->Ident; - - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using); - HashKey key = hash_string(name_token.string); - if (map_get(&entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration - error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); - } else { - map_set(&entity_map, key, e); - fields[field_index++] = e; - add_entity(c, c->context.scope, name, e); - } - add_entity_use(&c->info, name, e); - } - - - if (vd->is_using) { - Type *t = get_base_type(type_deref(type)); - if (!is_type_struct(t) && !is_type_raw_union(t)) { - Token name_token = vd->name_list->Ident; - error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string)); + if (node->kind == AstNode_UnionType) { + isize field_index = 0; + fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); + for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) { + if (decl->kind != AstNode_VarDecl) continue; + ast_node(vd, VarDecl, decl); + if (vd->kind != Declaration_Mutable) + continue; + Type *type = check_type(c, vd->type, NULL, cycle_checker); + + for (AstNode *name = vd->name_list; name != NULL; name = name->next) { + Token name_token = name->Ident; + + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type); + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); + } else { + map_set(&entity_map, key, e); + fields[field_index++] = e; + add_entity(c, c->context.scope, name, e); + } + add_entity_use(&c->info, name, e); + } + } + } else { + isize field_index = 0; + for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) { + if (decl->kind != AstNode_VarDecl) + continue; + ast_node(vd, VarDecl, decl); + if (vd->kind != Declaration_Mutable) + continue; + Type *type = check_type(c, vd->type, NULL, cycle_checker); + + if (vd->is_using) { + if (vd->name_count > 1) { + error(&c->error_collector, ast_node_token(vd->name_list), + "Cannot apply `using` to more than one of the same type"); + } } - populate_using_entity_map(c, node, type, &entity_map); + for (AstNode *name = vd->name_list; name != NULL; name = name->next) { + Token name_token = name->Ident; + + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using); + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); + } else { + map_set(&entity_map, key, e); + fields[field_index++] = e; + add_entity(c, c->context.scope, name, e); + } + add_entity_use(&c->info, name, e); + } + + + if (vd->is_using) { + Type *t = get_base_type(type_deref(type)); + if (!is_type_struct(t) && !is_type_raw_union(t)) { + Token name_token = vd->name_list->Ident; + error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a type", LIT(name_token.string)); + continue; + } + + populate_using_entity_map(c, node, type, &entity_map); + } } } - - - } void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) { - GB_ASSERT(node->kind == AstNode_StructType); GB_ASSERT(is_type_struct(struct_type)); ast_node(st, StructType, node); - // TODO(bill): check_struct_type and check_union_type are very similar so why not and try to merge them better - -isize field_count = 0; + isize field_count = 0; isize other_field_count = 0; for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) { switch (decl->kind) { @@ -357,6 +397,39 @@ isize field_count = 0; struct_type->Record.other_field_count = other_field_count; } +void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(is_type_union(union_type)); + ast_node(ut, UnionType, node); + + isize field_count = 1; + isize other_field_count = 0; + for (AstNode *decl = ut->decl_list; decl != NULL; decl = decl->next) { + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + if (vd->kind == Declaration_Mutable) { + field_count += vd->name_count; + } else { + other_field_count += vd->name_count; + } + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; +} + void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { GB_ASSERT(node->kind == AstNode_RawUnionType); GB_ASSERT(is_type_raw_union(union_type)); @@ -383,7 +456,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); - check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union")); + check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("raw union")); union_type->Record.fields = fields; union_type->Record.field_count = field_count; @@ -392,7 +465,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec } -void check_enum_type(Checker *c, Type *enum_type, AstNode *node) { +void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) { GB_ASSERT(node->kind == AstNode_EnumType); GB_ASSERT(is_type_enum(enum_type)); ast_node(et, EnumType, node); @@ -443,7 +516,11 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) { iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1)); } - Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, enum_type, iota); + Type *constant_type = enum_type; + if (named_type != NULL) { + constant_type = named_type; + } + Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, constant_type, iota); HashKey key = hash_string(name_token.string); if (map_get(&entity_map, key)) { @@ -764,7 +841,17 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c goto end; case_end; - case_ast_node(st, RawUnionType, e); + case_ast_node(ut, UnionType, e); + type = make_type_union(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_union_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(rut, RawUnionType, e); type = make_type_raw_union(c->allocator); set_base_type(named_type, type); check_open_scope(c, e); @@ -777,7 +864,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c case_ast_node(et, EnumType, e); type = make_type_enum(c->allocator); set_base_type(named_type, type); - check_enum_type(c, type, e); + check_enum_type(c, type, named_type, e); type->Record.node = e; goto end; case_end; @@ -1096,6 +1183,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { if (err_str != NULL) { error(&c->error_collector, op, "Cannot compare expression, %s", err_str); + x->type = t_untyped_bool; return; } @@ -1215,10 +1303,13 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) { return true; Type *x = operand->type; - Type *xb = get_enum_base_type(get_base_type(x)); - Type *yb = get_enum_base_type(get_base_type(y)); - if (are_types_identical(xb, yb)) + Type *xb = get_base_type(x); + Type *yb = get_base_type(y); + if (are_types_identical(xb, yb)) { return true; + } + xb = get_enum_base_type(x); + yb = get_enum_base_type(y); // Cast between booleans and integers diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 16453b4dc..ed8b16ef1 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -154,6 +154,8 @@ struct Type { }; }; +gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator()); + Type *get_base_type(Type *t) { for (;;) { if (t == NULL || t->kind != Type_Named) { @@ -210,6 +212,12 @@ Type *make_type_struct(gbAllocator a) { return t; } +Type *make_type_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Union; + return t; +} + Type *make_type_raw_union(gbAllocator a) { Type *t = alloc_type(a, Type_Record); t->Record.kind = TypeRecord_RawUnion; @@ -453,6 +461,8 @@ Type *base_vector_type(Type *t) { } return t; } + + b32 is_type_enum(Type *t) { t = get_base_type(t); return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum); @@ -461,6 +471,10 @@ b32 is_type_struct(Type *t) { t = get_base_type(t); return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct); } +b32 is_type_union(Type *t) { + t = get_base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union); +} b32 is_type_raw_union(Type *t) { t = get_base_type(t); return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion); @@ -484,7 +498,8 @@ b32 is_type_comparable(Type *t) { case Type_Pointer: return true; case Type_Record: { - if (is_type_struct(t)) { + if (false && is_type_struct(t)) { + // TODO(bill): Should I even allow this? for (isize i = 0; i < t->Record.field_count; i++) { if (!is_type_comparable(t->Record.fields[i]->type)) return false; @@ -492,7 +507,7 @@ b32 is_type_comparable(Type *t) { } else if (is_type_enum(t)) { return is_type_comparable(t->Record.enum_base); } - return true; + return false; } break; case Type_Array: return is_type_comparable(t->Array.elem); @@ -547,9 +562,10 @@ b32 are_types_identical(Type *x, Type *y) { return false; } } - return true; } + break; + case TypeRecord_Enum: return are_types_identical(x->Record.enum_base, y->Record.enum_base); } @@ -563,8 +579,9 @@ b32 are_types_identical(Type *x, Type *y) { break; case Type_Named: - if (y->kind == Type_Named) + if (y->kind == Type_Named) { return x->Named.base == y->Named.base; + } break; case Type_Tuple: @@ -673,24 +690,29 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se return sel; } if (is_type) { + + if (is_type_union(type)) { + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + String str = f->token.string; + + if (are_strings_equal(field_name, str)) { + return make_selection(f, NULL, i); + } + } + } + for (isize i = 0; i < type->Record.other_field_count; i++) { Entity *f = type->Record.other_fields[i]; GB_ASSERT(f->kind != Entity_Variable); String str = f->token.string; if (are_strings_equal(field_name, str)) { - if (is_type_enum(type)) { - GB_ASSERT(f->kind == Entity_Constant); - // NOTE(bill): Enums are constant expression - return make_selection(f, NULL, i); - } else { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } + return make_selection(f, NULL, i); } } - } else if (!is_type_enum(type)) { + } else if (!is_type_enum(type) && !is_type_union(type)) { for (isize i = 0; i < type->Record.field_count; i++) { Entity *f = type->Record.fields[i]; GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); @@ -761,6 +783,16 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return max; } break; + case TypeRecord_Union: { + i64 max = s.word_size; + for (isize i = 1; i < t->Record.field_count; i++) { + // NOTE(bill): field zero is null + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) + max = align; + } + return max; + } break; case TypeRecord_RawUnion: { i64 max = 1; for (isize i = 0; i < t->Record.field_count; i++) { @@ -868,6 +900,18 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type); } break; + case TypeRecord_Union: { + i64 count = t->Record.field_count; + i64 max = 0; + // NOTE(bill): Zeroth field is invalid + for (isize i = 1; i < count; i++) { + i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); + if (max < size) + max = size; + } + return type_size_of(s, allocator, t_int) + max; + } break; + case TypeRecord_RawUnion: { i64 count = t->Record.field_count; i64 max = 0; @@ -900,7 +944,6 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) return 0; } -gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator()); i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *t, Selection sel) { i64 offset = 0; @@ -952,7 +995,22 @@ gbString write_type_to_string(gbString str, Type *type) { Entity *f = type->Record.fields[i]; GB_ASSERT(f->kind == Entity_Variable); if (i > 0) - str = gb_string_appendc(str, ", "); + str = gb_string_appendc(str, "; "); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_Union: + str = gb_string_appendc(str, "union{"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + if (i > 0) { + str = gb_string_appendc(str, "; "); + } str = gb_string_append_length(str, f->token.string.text, f->token.string.len); str = gb_string_appendc(str, ": "); str = write_type_to_string(str, f->type); diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 6f63c0108..ecbf48d54 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -158,6 +158,10 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_fprintf(f, ">"); } break; + case TypeRecord_Union: { + i64 size_of_union = type_size_of(s, gb_heap_allocator(), t) - s.word_size; + ssa_fprintf(f, "{i%lld, [%lld x i8]}", word_bits, size_of_union); + } break; case TypeRecord_RawUnion: ssa_fprintf(f, "[%lld x i8]", type_size_of(s, gb_heap_allocator(), t)); break; @@ -172,7 +176,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_fprintf(f, "*"); break; case Type_Named: - if (is_type_struct(t)) { + if (is_type_struct(t) || is_type_union(t)) { ssa_print_encoded_local(f, t->Named.name); } else { ssa_print_type(f, s, get_base_type(t)); @@ -454,7 +458,6 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { ssa_fprintf(f, "%%%d = ", value->id); if (gb_is_between(bo->op.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { - if (is_type_string(elem_type)) { ssa_fprintf(f, "call "); ssa_print_type(f, m->sizes, t_bool); @@ -756,8 +759,9 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) { GB_ASSERT(v->kind == ssaValue_TypeName); Type *base_type = get_base_type(ssa_type(v)); - if (!is_type_struct(base_type)) + if (!is_type_struct(base_type) && !is_type_union(base_type)) { return; + } ssa_print_encoded_local(f, v->TypeName.name); ssa_fprintf(f, " = type "); ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type)); diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 24a350034..251a76de6 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1432,6 +1432,34 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); } + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, src_type)) { + gbAllocator allocator = proc->module->allocator; + ssaValue *parent = ssa_add_local_generated(proc, t); + ssaValue *tag = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i)); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, parent, v_zero32, t_int), tag); + + i64 int_size = proc->module->sizes.word_size; + i64 underlying_array_size = type_size_of(proc->module->sizes, allocator, ssa_type(parent)); + underlying_array_size -= int_size; + Type *array_type = make_type_array(allocator, t_u8, underlying_array_size); + ssaValue *data = ssa_emit_struct_gep(proc, parent, v_one32, array_type); + data = ssa_array_elem(proc, data); + + Type *tag_type = src_type; + Type *t_u8_ptr = make_type_pointer(allocator, t_u8); + Type *tag_type_ptr = make_type_pointer(allocator, tag_type); + ssaValue *underlying = ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, t_u8_ptr, tag_type_ptr)); + // underlying = ssa_emit_zero_gep(proc, underlying); + // ssa_set_type(underlying, src); + // ssa_emit_store(proc, underlying, value); + + return ssa_emit_load(proc, parent); + } + } + } // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's // subtype polymorphism casting @@ -1660,6 +1688,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case Token_GtEq: { ssaValue *left = ssa_build_expr(proc, be->left); ssaValue *right = ssa_build_expr(proc, be->right); + ssaValue *cmp = ssa_emit_comp(proc, be->op, left, right); return ssa_emit_conv(proc, cmp, default_type(tv->type)); } break; diff --git a/src/parser.cpp b/src/parser.cpp index 8ba398020..b2e4aba93 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -240,6 +240,11 @@ AST_NODE_KIND(_TypeBegin, "", struct{}) \ isize decl_count; \ b32 is_packed; \ }) \ + AST_NODE_KIND(UnionType, "union type", struct { \ + Token token; \ + AstNode *decl_list; \ + isize decl_count; \ + }) \ AST_NODE_KIND(RawUnionType, "raw union type", struct { \ Token token; \ AstNode *decl_list; \ @@ -395,6 +400,8 @@ Token ast_node_token(AstNode *node) { return node->VectorType.token; case AstNode_StructType: return node->StructType.token; + case AstNode_UnionType: + return node->UnionType.token; case AstNode_RawUnionType: return node->RawUnionType.token; case AstNode_EnumType: @@ -815,6 +822,15 @@ gb_inline AstNode *make_struct_type(AstFile *f, Token token, AstNode *decl_list, return result; } + +gb_inline AstNode *make_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) { + AstNode *result = make_node(f, AstNode_UnionType); + result->UnionType.token = token; + result->UnionType.decl_list = decl_list; + result->UnionType.decl_count = decl_count; + return result; +} + gb_inline AstNode *make_raw_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) { AstNode *result = make_node(f, AstNode_RawUnionType); result->RawUnionType.token = token; @@ -1674,8 +1690,7 @@ AstNode *parse_field_decl(AstFile *f) { type = make_bad_expr(f, ellipsis, f->cursor[0]); } else { if (name_count > 1) { - ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only 1 is allowed"); - type = make_bad_expr(f, ellipsis, f->cursor[0]); + ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only `..`"); } else { type = make_ellipsis(f, ellipsis, type); } @@ -1711,7 +1726,7 @@ AstNode *parse_parameter_list(AstFile *f, isize *param_count_) { } -AstNode *parse_struct_params(AstFile *f, isize *decl_count_) { +AstNode *parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allowed) { AstNode *decls = NULL; AstNode *decls_curr = NULL; isize decl_count = 0; @@ -1728,6 +1743,10 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) { ast_file_err(f, f->cursor[0], "Empty field declaration"); } + if (!using_allowed && is_using) { + ast_file_err(f, f->cursor[0], "Cannot apply `using` to members of a union"); + is_using = false; + } if (name_count > 1 && is_using) { ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type"); } @@ -1752,7 +1771,7 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) { DLIST_APPEND(decls, decls_curr, decl); if (decl->kind == AstNode_VarDecl) { decl_count += decl->VarDecl.name_count; - decl->VarDecl.is_using = is_using; + decl->VarDecl.is_using = is_using && using_allowed; if (decl->VarDecl.kind == Declaration_Mutable) { if (decl->VarDecl.value_count > 0) { @@ -1829,17 +1848,27 @@ AstNode *parse_identifier_or_type(AstFile *f) { Token open = expect_token(f, Token_OpenBrace); isize decl_count = 0; - AstNode *decls = parse_struct_params(f, &decl_count); + AstNode *decls = parse_struct_params(f, &decl_count, true); Token close = expect_token(f, Token_CloseBrace); return make_struct_type(f, token, decls, decl_count, is_packed); } break; + case Token_union: { + Token token = expect_token(f, Token_union); + Token open = expect_token(f, Token_OpenBrace); + isize decl_count = 0; + AstNode *decls = parse_struct_params(f, &decl_count, false); + Token close = expect_token(f, Token_CloseBrace); + + return make_union_type(f, token, decls, decl_count); + } + case Token_raw_union: { Token token = expect_token(f, Token_raw_union); Token open = expect_token(f, Token_OpenBrace); isize decl_count = 0; - AstNode *decls = parse_struct_params(f, &decl_count); + AstNode *decls = parse_struct_params(f, &decl_count, true); Token close = expect_token(f, Token_CloseBrace); return make_raw_union_type(f, token, decls, decl_count);