diff --git a/examples/demo.odin b/examples/demo.odin index 969b958fc..a795e75ae 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -4,20 +4,758 @@ main :: proc() { Entity :: type union { - FROG: struct { + Frog: struct { jump_height: f32 } - HELICOPTER: struct { + Helicopter: struct { weight: f32 blade_code: int } } - e: Entity - f: Entity = Entity.FROG{1}; - h: Entity = Entity.HELICOPTER{123, 4}; + using Entity + f: Entity = Frog{137} + h: Entity = Helicopter{123, 4} + match type ^f -> e { + case Frog: + print_string("Frog!\n") + print_f32(e.jump_height); nl() + e.jump_height = 69 + print_f32(e.jump_height); nl() + case Helicopter: + print_string("Helicopter!\n") + e.weight = 1337 + default: + print_string("Unknown!\n") + } } nl :: proc() { print_nl() } +/* +// Demo 001 +#load "basic.odin" +#load "game.odin" + +main :: proc() { + // _ = hellope() + // procedures() + // variables() + // constants() + // types() + // data_control() + // using_fields() + + run_game() +} + + +hellope :: proc() -> int { + print_string("Hellope, 世界\n") + return 1 +} + + +// Line comment +/* + Block Comment +*/ +/* + Nested /* + Block /* + Comment + */ + */ +*/ + +apple, banana, carrot: bool +box, carboard: bool = true, false +// hellope_value: int = hellope() // The procedure is ran just before `main` + +variables :: proc() { + i: int // initialized with zero value + j: int = 1 + x, y: int = 1, 2 + + // Type inference + apple, banana, 世界 := true, 123, "world" + + + // Basic Types of the Language + // + // bool + // + // i8 i16 i32 i64 i128 + // u8 u16 u32 u64 u128 + // + // f32 f64 + // + // int uint (size_of(int) == size_of(uint) == size_of(rawptr)) + // + // rawptr (equivalent to void * in C/C++) + // + // string + // + // byte - alias for u8 + // rune - alias for i32 // Unicode Codepoint + // + // "untyped" types can implicitly convert to any of the "typed" types + // Default Type + // untyped bool - bool + // untyped integer - int + // untyped float - f64 + // untyped pointer - rawptr + // untyped string - string + // untyped rune - rune/i32 + + + // Zero values + zero_numeric := 0 + zero_boolean := false + zero_pointer := null + zero_string1 := "" // Escaped string + zero_string2 := `` // Raw string + // Compound types have a different kind of zero value + + // Unary operators + // +a + // -a + // ~a + // !a + + // Binary operators + // a + b add + // a - b sub + // a ~ b xor + // a | b or + + // a * b mul + // a / b quo + // a % b mod + // a & b and + // a &~ b bitclear == a & (~b) + // a << b shl + // a >> b shr + + // a as Type // Type cast + // a transmute Type // Bit cast + + // a == b eq + // a != b ne + // a < b lt + // a > b gt + // a <= b le + // a >= b ge + +} + +procedures :: proc() { + add :: proc(x: int, y: int) -> int { + return x + y + } + print_int(add(3, 4)) // 7 + print_nl() + + add_v2 :: proc(x, y: int) -> int { + return x + y + } + + fibonacci :: proc(n: int) -> int { + if n < 2 { + return n + } + return fibonacci(n-1) + fibonacci(n-2) + } + print_int(fibonacci(12)); nl() + + + swap_strings :: proc(x, y: string) -> (string, string) { + return y, x + } + a, b := swap_strings("Hellope\n", "World\n") + print_string(a) + print_string(b) + + a, b = b, a // Quirk of grammar the of multiple assignments + // Swap variables + print_string(a) + print_string(b) + + // Not a hint like C/C++, it's mandatory (unless it cannot do it but it will warn) + proc1 :: proc(a, b: int) #inline { + print_int(a + b) + } + proc2 :: proc(a, b: int) #no_inline { + print_int(a + b) + } + + print_int(3 ''add 4) // Infix style + print_nl() + print_int(12 'fibonacci) // Postfix style + print_nl() +} + + +TAU :: 6.28318530718 + +constants :: proc() { + TAU :: 6.28318530718 // untyped float + WORLD_JAPANESE :: "世界" // untyped string + + TAU_32 : f32 : 6.28318530718 + TAU_AS_32 :: 6.28318530718 as f32 + + PI :: TAU / 2 + + CLOSE_TO_PI :: 3 + + DIFF :: (PI - CLOSE_TO_PI) / PI // Evaluated at compile time + + a := TAU // the constant's value becomes typed as f32 + b := CLOSE_TO_PI // the constant's value becomes typed as int + c := DIFF +} + +nl :: proc() { print_nl() } + +types :: proc() { + + x: int = 123 + y := x // y: int = x + // z: f32 = x // invalid + z: f32 = x as f32 + + + ptr_z := ^z // Pascal notation + ptr_z^ = 123 // Derefence Notation + w: f32 = ptr_z^ // 123 + print_f32(z); nl() + + // ^z - pointer to z + // z^ - z from pointer + + // Implicit conversion to and from rawptr + r_ptr: rawptr = ptr_z + ptr_z = r_ptr + + + + + f32_array: [12]f32 // Array of 12 f32 + f32_array[0] = 2 + f32_array[1] = 3 + // f32_array[-1] = 2 // Error - compile time check + // f32_array[13] = 2 // Error - compile time check + f32_array_len := len(f32_array) // builtin procedure + f32_array_cap := cap(f32_array) // == len(f32_array) + + + mda: [2][3][4]int // Column-major + // mda[x][y][z] + + + + api: [2]^f32 + papi: ^[2]^f32 + + + + + f32_slice: []f32 // Slice / Array reference + f32_slice = f32_array[0:5] + f32_slice = f32_array[:5] + f32_slice = f32_array[:] // f32_array[0:len(f32_array)-1] + + f32_slice = f32_array[1:5:7] // low:1, high:5, max:7 + // len: 5-1 == 4 + // cap: 7-1 == 6 + + + + append_success := append(^f32_slice, 1) + _ = append(^f32_slice, 2) + + _ = copy(f32_array[0:2], f32_array[2:4]) // You can use memcpy/memmove if you want + + + + + + + s := "Hellope World" + sub_string: string = s[5:10] + + + + + + v0: {4}f32 // Vector of 4 f32 + v0[0] = 1 + v0[1] = 3 + v0[2] = 6 + v0[3] = 10 + + v1 := v0 + v0 // Simd Arithmetic + v1 = v1 - v0 + v1 *= v0 // i.e. hadamard product + v1 /= v0 + + // builtin procedure + v2 := swizzle(v0, 3, 2, 1, 0) // {10, 6, 3, 1} + + v3: {4}bool = v0 == v2 + // LLVM rant? + + + + + + + + Vec4 :: type {4}f32 + Array3Int :: type [3]int + + Vec3 :: type struct { + x, y, z: f32 + } + + BinaryNode :: type struct { + left, right: ^BinaryNode // same format as procedure argument + data: rawptr + } + + AddProc :: type proc(a, b: int) -> int + + Packed :: type struct #packed { + a: u8 + b: u16 + c: u32 + } + assert(size_of(Packed) == 7) // builtin procedure + { + a, b: ^BinaryNode + a = alloc(size_of(BinaryNode)) as ^BinaryNode + b = alloc(size_of(BinaryNode)) as ^BinaryNode + + c := BinaryNode{a, b, null} + c.left^.data = null + c.left.data = null // No need to deference + + dealloc(a) + dealloc(b) + } + + { + MyInt :: type int + x: int = 1 + y: MyInt = 2 + // z := x + y // Failure - types cannot implicit convert* + z := x as MyInt + y // Type cast using `as` + } + + + { + // From: Quake III Arena + Q_rsqrt :: proc(number: f32) -> f32 { + i: i32 + x2, y: f32 + THREE_HALFS :: 1.5 + + x2 = number * 0.5 + y = number + i = (^y as ^i32)^ // evil floating point bit level hacking + i = 0x5f3759df - i>>1 // what the fuck? + y = (^i as ^f32)^ + y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration + // y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed + return y + } + + Q_rsqrt_v2 :: proc(number: f32) -> f32 { + THREE_HALFS :: 1.5 + + x2 := number * 0.5 + y := number + i := y transmute i32 // evil floating point bit level hacking + i = 0x5f3759df - i>>1 // what the fuck? + y = i transmute f32 + y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration + // y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed + return y + } + + // NOTE(bill): transmute only works if the size of the types are equal + + /* + // in C + union { + i32 i + f32 y + } + */ + } + + { // Enumeration + Thing :: type enum { + APPLE, + FROG, + TREE, + TOMB, + } + a := Thing.APPLE + + Sized :: type enum u64 { + APPLE, + FROG, + TREE, + TOMB, + } + assert(size_of(Sized) == size_of(u64)) + + Certain :: type enum { + APPLE = 3, + FROG, + TREE = 7, + TOMB, + } + assert(Certain.TOMB == 8) + } + + { // Untagged union + BitHack :: type raw_union { + i: i32 + f: f32 + } + b: BitHack + b.f = 123 + print_int(b.i as int); print_nl() + + + + // Manually tagged union + + EntityKind :: type enum { + Invalid, + Constant, + Variable, + TypeName, + Procedure, + Builtin, + Count, + } + + Entity :: type struct { + kind: EntityKind + guid: u64 + + // Other data + + /*using*/ + data: union { + constant: struct{} + variable: struct{ + visited, is_field, used, anonymous: bool + } + procedure: struct { used: bool } + buitlin: struct { id: i32 } + } + } + + + // NOTE(bill): Tagged unions are not added yet but are planned + } + + + + { // Compound Literals + a := [3]int{1, 2, 3} + b := [3]int{} + c := [..]int{1, 2, 3} + + d := []int{1, 2, 3} // slice + + e := {4}f32{1, 2, 3, 4} + f := {4}f32{1} // broadcasts to all + // g := {4}f32{1, 2} // require either 1 or 4 elements + + Vec2 :: type {2}f32 + + h := Vec2{1, 2} + + i := Vec2{5} * h // For strong type safety + // FORENOTE: 5 * h was originally allowed but it was an edge case in the + // compiler I didn't think it was enough to justify have it it. + + print_f32(i[0]); print_rune(#rune ",") + print_f32(i[1]); print_nl() + } + + + + { // First class procedures + + do_thing :: proc(p: proc(a, b: int) -> int) { + print_int(p(3, 4)); nl() + } + + add :: proc(a, b: int) -> int { + return a + b + } + + + add_lambda := proc(a, b: int) -> int { + return a - b + } // note semicolon + + do_thing(add) + do_thing(add_lambda) + do_thing(proc(a, b: int) -> int { // Anonymous + return a * b + }) + } + + + + { // strings and runes + escaped := "Hellope World\n" + raw := `Hellope World\n` + print_string(escaped) + print_string(raw); nl() + + // Crap shader example + shader_string := +`#version 410 + +layout (location = 0) in vec3 a_position +layout (location = 1) in vec3 a_normal; +layout (location = 2) in vec2 a_tex_coord; + +out vec3 v_position; +out vec3 v_normal; +out vec2 v_tex_coord; + +uniform mat4 u_model_view; +uniform mat3 u_normal; +uniform mat4 u_proj; +uniform mat4 u_mvp; + +void main() { + v_tex_coord = a_tex_coord; + v_normal = normalize(u_normal * a_normal); + v_position = vec3(u_model_view * vec4(a_position, 1.0)); + + gl_Position = u_mvp * vec4(a_position, 1.0); +}`; + + + hearts1 := #rune "💕"; + hearts2 := #rune "\U0001f495"; // 32 bit + hearts3 := #rune "\xf0\x9f\x92\x95"; + + 㐒 := #rune "㐒"; + 㐒16 := #rune "\u4db5"; // 16 bit but will be `rune` + // String ideas "nicked" from Go, so far. I think I might change how some of it works later. + } + + + { // size, align, offset + Thing :: type struct { + a: u8; + b: u16; + c, d, e: u32; + } + + s := size_of(Thing); + a := align_of(Thing); + o := offset_of(Thing, b); + + t: Thing; + + sv := size_of_val(t); + av := align_of_val(t); + ov := offset_of_val(t.b); + } +} + +data_control :: proc() { + sum := 0 + for i := 0; i < 12; i++ { + sum += 1 + } + print_string("sum = "); print_int(sum); nl() + + sum = 1 + for ; sum < 1000000; { + sum += sum + } + print_string("sum = "); print_int(sum); nl() + + sum = 1 + for sum < 1000000 { + sum += sum + } + print_string("sum = "); print_int(sum); nl() + + // loop + // for { } == for true {} + + // Question: Should I separate all these concepts and rename it? + // + // range - iterable + // for - c style + // while + // loop - while true + + // Notes: + // conditions _must_ a boolean expression + // i++ and i-- are statements, not expressions + + + x := 2 + if x < 3 { + print_string("x < 2\n") + } + + // Unified initializer syntax - same as for statements + if x := 2; x < 3 { + print_string("x < 2\n") + } + + if x := 4; x < 3 { + print_string("Never called\n") + } else { + print_string("This is called\n") + } + + { // String comparison + a := "Hellope" + b := "World" + if a < b { + print_string("a < b\n") + } + if a != b { + print_string("a != b\n") + } + + } + + + + + { // Defer statement + defer print_string("日本語\n") + print_string("Japanese\n") + } + + { + defer print_string("1\n") + defer print_string("2\n") + defer print_string("3\n") + } + + { + prev_allocator := context.allocator + context.allocator = __default_allocator() + defer context.allocator = prev_allocator + + File :: type struct { filename: string } + FileError :: type int; + open_file :: proc(filename: string) -> (File, FileError) { + return File{}, 0 + } + close_file :: proc(f: ^File) {} + f, err := open_file("Test") + if err != 0 { + // handle error + } + defer close_file(^f) + } + + for i := 0; i < 100; i++ { + blah := new_slice(int, 100) + defer { + defer print_string("!") + defer print_string("dealloc") + delete(blah) + } + + if i == 3 { + // defers called + continue + } + + if i == 5 { + // defers called + return // End of procedure + } + + if i == 8 { + // defers called + break // never happens + } + } + + defer print_string("It'll never happen, mate 1") + print_string("It'll never happen, mate 2") + print_string("It'll never happen, mate 3") +} + + +using_fields :: proc() { + { // Everyday stuff + Vec3 :: type struct { x, y, z: f32; } + + Entity :: type struct { + name: string; + using pos: Vec3; + vel: Vec3; + } + t: Entity; + t.y = 456; + print_f32(t.y); print_nl(); + print_f32(t.pos.y); print_nl(); + print_f32(t.vel.y); print_nl(); + + + Frog :: type struct { // Subtype (kind of) + using entity: Entity; + colour: u32; + jump_height: f32; + } + + f: Frog; + f.y = 1337; + print_f32(f.y); print_nl(); + print_f32(f.pos.y); print_nl(); + print_f32(f.vel.y); print_nl(); + + + Buffalo :: type struct { + using entity: Entity; + speed: f32; + noise_level: f32; + } + } + + + { // Crazy Shit + Vec2 :: type raw_union { + using _xy: struct {x, y: f32}; + e: [2]f32; + v: {2}f32; + } + + Entity :: type struct { + using pos: ^Vec2; + name: string; + } + t: Entity; + t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)? + t.x = 123; + print_f32(t._xy.x); print_nl(); + print_f32(t.pos.x); print_nl(); + print_f32(t.pos._xy.x); print_nl(); + } +} +*/ diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 8c95281fc..04b68eb9b 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -197,12 +197,13 @@ struct CheckerContext { // NOTE(bill): Symbol tables struct CheckerInfo { - Map types; // Key: AstNode * | Expression -> Type (and value) - Map definitions; // Key: AstNode * | Identifier -> Entity - Map uses; // Key: AstNode * | Identifier -> Entity - Map scopes; // Key: AstNode * | Node -> Scope - Map untyped; // Key: AstNode * | Expression -> ExpressionInfo - Map entities; // Key: Entity * + Map types; // Key: AstNode * | Expression -> Type (and value) + Map definitions; // Key: AstNode * | Identifier -> Entity + Map uses; // Key: AstNode * | Identifier -> Entity + Map scopes; // Key: AstNode * | Node -> Scope + Map untyped; // Key: AstNode * | Expression -> ExpressionInfo + Map entities; // Key: Entity * + Map foreign_procs; // Key: String }; struct Checker { @@ -424,12 +425,13 @@ void init_universal_scope(void) { void init_checker_info(CheckerInfo *i) { gbAllocator a = gb_heap_allocator(); - map_init(&i->types, a); - map_init(&i->definitions, a); - map_init(&i->uses, a); - map_init(&i->scopes, a); - map_init(&i->entities, a); - map_init(&i->untyped, a); + map_init(&i->types, a); + map_init(&i->definitions, a); + map_init(&i->uses, a); + map_init(&i->scopes, a); + map_init(&i->entities, a); + map_init(&i->untyped, a); + map_init(&i->foreign_procs, a); } @@ -440,6 +442,7 @@ void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->scopes); map_destroy(&i->entities); map_destroy(&i->untyped); + map_destroy(&i->foreign_procs); } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 60788a16c..a3ffd6ccc 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -296,12 +296,16 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, ast_node(vd, VarDecl, decl); if (vd->kind != Declaration_Mutable) continue; - Type *type = check_type(c, vd->type, NULL, cycle_checker); + Type *base_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; + Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL); Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type); + type->Named.type_name = e; + add_entity(c, c->context.scope, name, e); + HashKey key = hash_string(name_token.string); if (map_get(&entity_map, key) != NULL) { // TODO(bill): Scope checking already checks the declaration @@ -309,7 +313,6 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, } 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); } @@ -3154,7 +3157,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } - check_index_value(c, ie->index, max_count, NULL); + i64 index = 0; + b32 ok = check_index_value(c, ie->index, max_count, &index); + case_end; diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 803e777de..78a98a648 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -127,6 +127,21 @@ b32 check_is_terminating(AstNode *node) { } return has_default; case_end; + + case_ast_node(ms, TypeMatchStmt, node); + b32 has_default = false; + for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) { + ast_node(cc, CaseClause, clause); + if (cc->list == NULL) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; } return false; @@ -475,6 +490,31 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) { } } + if (is_foreign) { + auto *fp = &c->info.foreign_procs; + auto *proc_decl = &d->proc_decl->ProcDecl; + String name = proc_decl->name->Ident.string; + if (proc_decl->foreign_name.len > 0) { + name = proc_decl->foreign_name; + } + HashKey key = hash_string(name); + auto *found = map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = get_base_type(e->type); + Type *other_type = get_base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(&c->error_collector, ast_node_token(d->proc_decl), + "Redeclaration of #foreign procedure `%.*s` with different type signatures\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } + } else { + map_set(fp, key, e); + } + } + } void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) { @@ -1018,8 +1058,122 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { check_close_scope(c); i++; } + case_end; + + case_ast_node(ms, TypeMatchStmt, node); + Operand x = {}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + defer (check_close_scope(c)); + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, make_string("type match expression")); + if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) { + gbString str = type_to_string(x.type); + defer (gb_string_free(str)); + error(&c->error_collector, ast_node_token(x.expr), + "Expected a pointer to a union for this type match expression, got `%s`", str); + break; + } + Type *base_union = get_base_type(type_deref(x.type)); + + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) { + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(c, CaseClause, stmt); + if (c->list_count == 0) { + default_stmt = stmt; + } + } else { + error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause"); + } + + if (default_stmt != NULL) { + if (first_default != NULL) { + TokenPos pos = ast_node_token(first_default).pos; + error(&c->error_collector, ast_node_token(stmt), + "multiple `default` clauses\n" + "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + } else { + first_default = default_stmt; + } + } + } + + if (ms->var->kind != AstNode_Ident) { + break; + } + + + Map seen = {}; + map_init(&seen, gb_heap_allocator()); + defer (map_destroy(&seen)); + + + + for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) { + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + AstNode *type_expr = cc->list; + Type *tag_type = NULL; + if (type_expr != NULL) { // Otherwise it's a default expression + Operand y = {}; + check_expr_or_type(c, &y, type_expr); + b32 tag_type_found = false; + for (isize i = 0; i < base_union->Record.field_count; i++) { + Entity *f = base_union->Record.fields[i]; + if (are_types_identical(f->type, y.type)) { + tag_type_found = true; + break; + } + } + if (!tag_type_found) { + gbString type_str = type_to_string(y.type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(y.expr), + "Unknown tag type, got `%s`", type_str); + continue; + } + tag_type = y.type; + + HashKey key = hash_pointer(y.type); + auto *found = map_get(&seen, key); + if (found) { + TokenPos pos = cc->token.pos; + gbString expr_str = expr_to_string(y.expr); + error(&c->error_collector, + ast_node_token(y.expr), + "Duplicate type case `%s`\n" + "\tprevious type case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + break; + } + map_set(&seen, key, cast(b32)true); + + } + + check_open_scope(c, stmt); + if (tag_type != NULL) { + // NOTE(bill): Dummy type + Type *tag_ptr_type = make_type_pointer(c->allocator, tag_type); + Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type); + add_entity(c, c->context.scope, ms->var, tag_var); + } + check_stmt_list(c, cc->stmts, cc->stmt_count, mod_flags); + check_close_scope(c); + } case_end; @@ -1096,27 +1250,27 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } } else if (is_type_struct(t)) { Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); - if (found != NULL) { - gb_for_array(i, (*found)->elements.entries) { - Entity *f = (*found)->elements.entries[i].value; - Entity *found = scope_insert_entity(c->context.scope, f); - if (found != NULL) { - error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); - return; - } - f->using_parent = e; + GB_ASSERT(found != NULL); + gb_for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries[i].value; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + return; } - } else { - for (isize i = 0; i < t->Record.other_field_count; i++) { - // TODO(bill): using field types too - Entity *f = t->Record.other_fields[i]; - Entity *found = scope_insert_entity(c->context.scope, f); - if (found != NULL) { - error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); - return; - } - f->using_parent = e; + f->using_parent = e; + } + } else if (is_type_union(t)) { + Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + gb_for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries[i].value; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + return; } + f->using_parent = e; } } } break; diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 735dfd580..06b6952d0 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -31,6 +31,27 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) { if (err != gbFileError_None) return false; +#if 0 + Map type_map; // Key: Type * + map_init(&type_map, gb_heap_allocator()); + i32 index = 0; + gb_for_array(i, c->info.types.entries) { + TypeAndValue tav = c->info.types.entries[i].value; + Type *type = tav.type; + HashKey key = hash_pointer(type); + auto found = map_get(&type_map, key); + if (!found) { + map_set(&type_map, key, index); + index++; + } + } + gb_for_array(i, type_map.entries) { + auto *e = &type_map.entries[i]; + Type *t = cast(Type *)cast(uintptr)e->key.key; + gb_printf("%s\n", type_to_string(t)); + } +#endif + return true; } @@ -112,7 +133,10 @@ void ssa_gen_code(ssaGen *s) { p->Proc.tags = pd->tags; map_set(&m->values, hash_pointer(e), p); - map_set(&m->members, hash_string(name), p); + HashKey hash_name = hash_string(name); + if (map_get(&m->members, hash_name) == NULL) { + map_set(&m->members, hash_name, p); + } } break; } } diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 383800f96..e0ab7edc4 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -319,22 +319,22 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { ssa_fprintf(f, "call void @" SSA_STARTUP_RUNTIME_PROC_NAME "()\n"); } break; + case ssaInstr_Comment: + ssa_fprintf(f, "; %.*s\n", LIT(instr->Comment.text)); + break; + case ssaInstr_Local: { Type *type = instr->Local.entity->type; ssa_fprintf(f, "%%%d = alloca ", value->id); ssa_print_type(f, m->sizes, type); - ssa_fprintf(f, ", align %lld ", type_align_of(m->sizes, m->allocator, type)); - { - String str = instr->Local.entity->token.string; - if (str.len > 0) - ssa_fprintf(f, "; %.*s", LIT(instr->Local.entity->token.string)); + ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + if (instr->Local.zero_initialized) { + ssa_fprintf(f, "\tstore "); + ssa_print_type(f, m->sizes, type); + ssa_fprintf(f, " zeroinitializer, "); + ssa_print_type(f, m->sizes, type); + ssa_fprintf(f, "* %%%d\n", value->id); } - ssa_fprintf(f, "\n"); - ssa_fprintf(f, "\tstore "); - ssa_print_type(f, m->sizes, type); - ssa_fprintf(f, " zeroinitializer, "); - ssa_print_type(f, m->sizes, type); - ssa_fprintf(f, "* %%%d\n", value->id); } break; case ssaInstr_Store: { @@ -713,9 +713,9 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { auto *params = &proc_type->params->Tuple; for (isize i = 0; i < params->variable_count; i++) { Entity *e = params->variables[i]; - if (i > 0) - + if (i > 0) { ssa_fprintf(f, ", "); + } ssa_print_type(f, m->sizes, e->type); ssa_fprintf(f, " %%%.*s", LIT(e->token.string)); } @@ -724,14 +724,18 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { ssa_fprintf(f, ") "); if (proc->tags != 0) { - if (proc->tags & ProcTag_inline) + if (proc->tags & ProcTag_inline) { ssa_fprintf(f, "alwaysinline "); - if (proc->tags & ProcTag_no_inline) + } + if (proc->tags & ProcTag_no_inline) { ssa_fprintf(f, "noinline "); + } - if (proc->tags & ProcTag_foreign) + if (proc->tags & ProcTag_foreign) { + // TODO(bill): Set calling convention ssa_fprintf(f, "; foreign\n"); + } } if (proc->body != NULL) { @@ -803,7 +807,10 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { ssa_fprintf(f, "thread_local "); } if (g->is_constant) { - ssa_fprintf(f, "private constant "); + if (g->is_private) { + ssa_fprintf(f, "private "); + } + ssa_fprintf(f, "constant "); } else { ssa_fprintf(f, "global "); } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index db5201b9e..5d1a05f7a 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -72,6 +72,7 @@ struct ssaProcedure { #define SSA_INSTR_KINDS \ SSA_INSTR_KIND(Invalid), \ + SSA_INSTR_KIND(Comment), \ SSA_INSTR_KIND(Local), \ SSA_INSTR_KIND(Store), \ SSA_INSTR_KIND(Load), \ @@ -138,9 +139,14 @@ struct ssaInstr { Type *type; union { + struct { + String text; + } Comment; + struct { Entity *entity; - Type *type; + Type * type; + b32 zero_initialized; } Local; struct { ssaValue *address; @@ -251,6 +257,7 @@ struct ssaValue { } TypeName; struct { b32 is_constant; + b32 is_private; b32 is_thread_local; Entity * entity; Type * type; @@ -435,11 +442,12 @@ ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) { } -ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e) { +ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) { ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local); ssaInstr *i = &v->Instr; i->Local.entity = e; i->Local.type = make_type_pointer(p->module->allocator, e->type); + i->Local.zero_initialized = zero_initialized; ssa_module_add_value(p->module, e, v); return v; } @@ -589,6 +597,11 @@ ssaValue *ssa_make_instr_no_op(ssaProcedure *p) { } +ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment); + v->Instr.Comment.text = text; + return v; +} @@ -681,15 +694,21 @@ ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue return ssa_emit(p, ssa_make_instr_select(p, cond, t, f)); } - -ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e) { - return ssa_emit(proc, ssa_make_instr_local(proc, e)); +ssaValue *ssa_emit_comment(ssaProcedure *p, String text) { + return ssa_emit(p, ssa_make_instr_comment(p, text)); } -ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name) { + +ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) { + return ssa_emit(proc, ssa_make_instr_local(proc, e, zero_initialized)); +} + +ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) { Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name)); if (found) { - return ssa_add_local(proc, *found); + Entity *e = *found; + ssa_emit_comment(proc, e->token.string); + return ssa_add_local(proc, e, zero_initialized); } return NULL; } @@ -705,7 +724,7 @@ ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { scope, empty_token, type); - return ssa_emit(proc, ssa_make_instr_local(proc, entity)); + return ssa_emit(proc, ssa_make_instr_local(proc, entity, true)); } ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) { @@ -772,6 +791,7 @@ void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) { } gb_array_append(proc->blocks, b); proc->curr_block = b; + ssa_emit_comment(proc, make_string("defer")); ssa_build_stmt(proc, d.stmt); } @@ -781,7 +801,7 @@ void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferKind kind, ssaBlock *block while (i --> 0) { ssaDefer d = proc->defer_stmts[i]; if (kind == ssaDefer_Return) { - ssa_build_defer_stmt(proc, d); + ssa_build_defer_stmt(proc, d); } else if (kind == ssaDefer_Default) { if (proc->scope_index == d.scope_index && d.scope_index > 1) { @@ -899,6 +919,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) { ssaInstr *instr = &value->Instr; // NOTE(bill): Ignore non-returning instructions switch (instr->kind) { + case ssaInstr_Comment: case ssaInstr_Store: case ssaInstr_Br: case ssaInstr_Ret: @@ -1217,6 +1238,7 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) { Type *type = make_type_array(a, t_u8, value.value_string.len); Entity *entity = make_entity_constant(a, NULL, token, type, value); ssaValue *g = ssa_make_value_global(a, entity, ssa_make_value_constant(a, type, value)); + g->Global.is_private = true; map_set(&proc->module->values, hash_pointer(entity), g); map_set(&proc->module->members, hash_string(name), g); @@ -1368,6 +1390,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg for (isize i = 0; i < dst->Record.field_count; i++) { Entity *f = dst->Record.fields[i]; if (are_types_identical(f->type, src_type)) { + ssa_emit_comment(proc, make_string("union - child to parent")); 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)); @@ -1404,6 +1427,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg // NOTE(bill): It can be casted Selection sel = lookup_field(sb, field_name, false); if (sel.entity != NULL) { + ssa_emit_comment(proc, make_string("cast - polymorphism")); if (src_is_ptr) { value = ssa_emit_load(proc, value); } @@ -1621,12 +1645,15 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case Token_as: + ssa_emit_comment(proc, make_string("cast - as")); return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type); case Token_transmute: + ssa_emit_comment(proc, make_string("cast - transmute")); return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type); case Token_down_cast: + ssa_emit_comment(proc, make_string("cast - down_cast")); return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type); default: @@ -1660,6 +1687,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case_ast_node(cl, CompoundLit, expr); + ssa_emit_comment(proc, make_string("CompoundLit")); Type *type = type_of_expr(proc->module->info, expr); Type *base_type = get_base_type(type); ssaValue *v = ssa_add_local_generated(proc, type); @@ -1787,6 +1815,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue Entity *e = *found; switch (e->Builtin.id) { case BuiltinProc_new: { + ssa_emit_comment(proc, make_string("new")); // new :: proc(Type) -> ^Type gbAllocator allocator = proc->module->allocator; @@ -1805,6 +1834,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_new_slice: { + ssa_emit_comment(proc, make_string("new_slice")); // new_slice :: proc(Type, len: int[, cap: int]) -> ^Type gbAllocator allocator = proc->module->allocator; @@ -1844,6 +1874,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_delete: { + ssa_emit_comment(proc, make_string("delete")); // delete :: proc(ptr: ^Type) // delete :: proc(slice: []Type) gbAllocator allocator = proc->module->allocator; @@ -1863,6 +1894,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case BuiltinProc_assert: { + ssa_emit_comment(proc, make_string("assert")); ssaValue *cond = ssa_build_expr(proc, ce->arg_list); GB_ASSERT(is_type_boolean(ssa_type(cond))); @@ -1905,6 +1937,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_len: { + ssa_emit_comment(proc, make_string("len")); // len :: proc(v: Type) -> int // NOTE(bill): len of an array is a constant expression ssaValue *v = ssa_build_expr(proc, ce->arg_list); @@ -1915,6 +1948,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_slice_len(proc, v); } break; case BuiltinProc_cap: { + ssa_emit_comment(proc, make_string("cap")); // cap :: proc(v: Type) -> int // NOTE(bill): cap of an array is a constant expression ssaValue *v = ssa_build_expr(proc, ce->arg_list); @@ -1922,6 +1956,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_slice_cap(proc, v); } break; case BuiltinProc_copy: { + ssa_emit_comment(proc, make_string("copy")); // copy :: proc(dst, src: []Type) -> int AstNode *dst_node = ce->arg_list; AstNode *src_node = ce->arg_list->next; @@ -1955,6 +1990,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return len; } break; case BuiltinProc_append: { + ssa_emit_comment(proc, make_string("append")); // append :: proc(s: ^[]Type, item: Type) -> bool AstNode *sptr_node = ce->arg_list; AstNode *item_node = ce->arg_list->next; @@ -1993,7 +2029,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue item = ssa_emit_ptr_offset(proc, item, v_zero); item = ssa_emit_conv(proc, item, t_rawptr, true); - + ssa_emit(proc, ssa_make_instr_copy_memory(proc, offset, item, byte_count, 1, false)); // Increment slice length @@ -2010,6 +2046,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_swizzle: { + ssa_emit_comment(proc, make_string("swizzle")); ssaValue *vector = ssa_build_expr(proc, ce->arg_list); isize index_count = ce->arg_list_count-1; if (index_count == 0) { @@ -2030,12 +2067,14 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_ptr_offset: { + ssa_emit_comment(proc, make_string("ptr_offset")); ssaValue *ptr = ssa_build_expr(proc, ce->arg_list); ssaValue *offset = ssa_build_expr(proc, ce->arg_list->next); return ssa_emit_ptr_offset(proc, ptr, offset); } break; case BuiltinProc_ptr_sub: { + ssa_emit_comment(proc, make_string("ptr_sub")); ssaValue *ptr_a = ssa_build_expr(proc, ce->arg_list); ssaValue *ptr_b = ssa_build_expr(proc, ce->arg_list->next); Type *ptr_type = get_base_type(ssa_type(ptr_a)); @@ -2054,6 +2093,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_slice_ptr: { + ssa_emit_comment(proc, make_string("slice_ptr")); ssaValue *ptr = ssa_build_expr(proc, ce->arg_list); ssaValue *len = ssa_build_expr(proc, ce->arg_list->next); ssaValue *cap = len; @@ -2075,6 +2115,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_min: { + ssa_emit_comment(proc, make_string("min")); ssaValue *x = ssa_build_expr(proc, ce->arg_list); ssaValue *y = ssa_build_expr(proc, ce->arg_list->next); Type *t = get_base_type(ssa_type(x)); @@ -2084,6 +2125,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_max: { + ssa_emit_comment(proc, make_string("max")); ssaValue *x = ssa_build_expr(proc, ce->arg_list); ssaValue *y = ssa_build_expr(proc, ce->arg_list->next); Type *t = get_base_type(ssa_type(x)); @@ -2093,6 +2135,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; case BuiltinProc_abs: { + ssa_emit_comment(proc, make_string("abs")); Token lt = {Token_Lt}; Token sub = {Token_Sub}; @@ -2165,6 +2208,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } if (variadic) { + ssa_emit_comment(proc, make_string("variadic call argument generation")); gbAllocator allocator = proc->module->allocator; Type *slice_type = pt->variables[type->param_count-1]->type; Type *elem_type = get_base_type(slice_type)->Slice.elem; @@ -2218,6 +2262,8 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { if (tv->value.kind != ExactValue_Invalid) { if (tv->value.kind == ExactValue_String) { + ssa_emit_comment(proc, make_string("Emit string constant")); + // TODO(bill): Optimize by not allocating everytime if (tv->value.value_string.len > 0) { ssaValue *global_array = ssa_add_global_string_array(proc, tv->value); @@ -2297,6 +2343,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_end; case_ast_node(se, SelectorExpr, expr); + ssa_emit_comment(proc, make_string("SelectorExpr")); Type *type = get_base_type(type_of_expr(proc->module->info, se->expr)); Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.string, false); @@ -2320,6 +2367,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_ast_node(be, BinaryExpr, expr); switch (be->op.kind) { case Token_as: { + ssa_emit_comment(proc, make_string("Cast - as")); // HACK(bill): Do have to make new variable to do this? // NOTE(bill): Needed for dereference of pointer conversion Type *type = type_of_expr(proc->module->info, expr); @@ -2328,6 +2376,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { return ssa_make_addr(v, expr); } case Token_transmute: { + ssa_emit_comment(proc, make_string("Cast - transmute")); // HACK(bill): Do have to make new variable to do this? // NOTE(bill): Needed for dereference of pointer conversion Type *type = type_of_expr(proc->module->info, expr); @@ -2342,6 +2391,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_end; case_ast_node(ie, IndexExpr, expr); + ssa_emit_comment(proc, make_string("IndexExpr")); ssaValue *v = NULL; Type *t = get_base_type(type_of_expr(proc->module->info, ie->expr)); ssaValue *elem = NULL; @@ -2387,6 +2437,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_end; case_ast_node(se, SliceExpr, expr); + ssa_emit_comment(proc, make_string("SliceExpr")); gbAllocator a = proc->module->allocator; ssaValue *low = v_zero; ssaValue *high = NULL; @@ -2412,7 +2463,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { ssaValue *len = ssa_emit_arith(proc, op_sub, high, low, t_int); ssaValue *cap = ssa_emit_arith(proc, op_sub, max, low, t_int); ssaValue *slice = ssa_add_local_generated(proc, slice_type); - + ssaValue *gep0 = ssa_emit_struct_gep(proc, slice, v_zero32, ssa_type(elem)); ssaValue *gep1 = ssa_emit_struct_gep(proc, slice, v_one32, t_int); ssaValue *gep2 = ssa_emit_struct_gep(proc, slice, v_two32, t_int); @@ -2520,7 +2571,7 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { map_set(&m->members, hash_string(name), t); Type *bt = get_base_type(e->type); - if (is_type_struct(bt)) { + if (bt->kind == Type_Record) { auto *s = &bt->Record; for (isize j = 0; j < s->other_field_count; j++) { Entity *field = s->other_fields[j]; @@ -2541,6 +2592,29 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { } } } + + if (is_type_union(bt)) { + auto *s = &bt->Record; + // NOTE(bill): Zeroth entry is null (for `match type` stmts) + for (isize j = 1; j < s->field_count; j++) { + Entity *field = s->fields[j]; + if (field->kind == Entity_TypeName) { + // HACK(bill): Override name of type so printer prints it correctly + auto *tn = &field->type->Named; + String cn = field->token.string; + isize len = name.len + 1 + cn.len; + String child = {NULL, len}; + child.text = gb_alloc_array(m->allocator, u8, len); + isize i = 0; + gb_memcopy(child.text+i, name.text, name.len); + i += name.len; + child.text[i++] = '.'; + gb_memcopy(child.text+i, cn.text, cn.len); + tn->name = child; + ssa_gen_global_type_name(m, field, tn->name); + } + } + } } @@ -2575,7 +2649,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { for (AstNode *name = vd->name_list; name != NULL; name = name->next) { ssaAddr lval = ssa_make_addr(NULL, NULL); if (!ssa_is_blank_ident(name)) { - ssa_add_local_for_identifier(proc, name); + ssa_add_local_for_identifier(proc, name, false); lval = ssa_build_addr(proc, name); GB_ASSERT(lval.addr != NULL); } @@ -2597,7 +2671,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { } else if (vd->value_count == 0) { // declared and zero-initialized for (AstNode *name = vd->name_list; name != NULL; name = name->next) { if (!ssa_is_blank_ident(name)) { - ssa_add_local_for_identifier(proc, name); + ssa_add_local_for_identifier(proc, name, true); } } } else { // Tuple(s) @@ -2611,7 +2685,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { for (AstNode *name = vd->name_list; name != NULL; name = name->next) { ssaAddr lval = ssa_make_addr(NULL, NULL); if (!ssa_is_blank_ident(name)) { - ssa_add_local_for_identifier(proc, name); + ssa_add_local_for_identifier(proc, name, false); lval = ssa_build_addr(proc, name); } @@ -2669,6 +2743,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_array_append(proc->children, &value->Proc); ssa_build_proc(value, proc); } else { + // FFI - Foreign function interace String original_name = pd->name->Ident.string; String name = original_name; if (pd->foreign_name.len > 0) { @@ -2676,14 +2751,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { } auto *info = proc->module->info; - Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); - GB_ASSERT(found != NULL); - Entity *e = *found; + Entity *e = *map_get(&info->definitions, hash_pointer(pd->name)); + Entity *f = *map_get(&info->foreign_procs, hash_string(name)); ssaValue *value = ssa_make_value_procedure(proc->module->allocator, proc->module, e->type, pd->type, pd->body, name); ssa_module_add_value(proc->module, e, value); - gb_array_append(proc->children, &value->Proc); ssa_build_proc(value, proc); + if (e == f) { + // NOTE(bill): Don't do mutliple declarations in the IR + gb_array_append(proc->children, &value->Proc); + } } case_end; @@ -2709,6 +2786,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(ids, IncDecStmt, node); + ssa_emit_comment(proc, make_string("IncDecStmt")); Token op = ids->op; if (op.kind == Token_Increment) { op.kind = Token_Add; @@ -2722,6 +2800,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(as, AssignStmt, node); + ssa_emit_comment(proc, make_string("AssignStmt")); switch (as->op.kind) { case Token_Eq: { gbArray(ssaAddr) lvals; @@ -2811,6 +2890,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(ds, DeferStmt, node); + ssa_emit_comment(proc, make_string("DeferStmt")); isize scope_index = proc->scope_index; if (ds->stmt->kind == AstNode_BlockStmt) scope_index--; @@ -2819,6 +2899,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(rs, ReturnStmt, node); + ssa_emit_comment(proc, make_string("ReturnStmt")); ssaValue *v = NULL; auto *return_type_tuple = &proc->type->Proc.results->Tuple; isize return_count = proc->type->Proc.result_count; @@ -2850,6 +2931,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(is, IfStmt, node); + ssa_emit_comment(proc, make_string("IfStmt")); if (is->init != NULL) { ssaBlock *init = ssa_add_block(proc, node, make_string("if.init")); ssa_emit_jump(proc, init); @@ -2889,6 +2971,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(fs, ForStmt, node); + ssa_emit_comment(proc, make_string("ForStmt")); if (fs->init != NULL) { ssaBlock *init = ssa_add_block(proc, node, make_string("for.init")); ssa_emit_jump(proc, init); @@ -2938,6 +3021,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; case_ast_node(ms, MatchStmt, node); + ssa_emit_comment(proc, make_string("MatchStmt")); if (ms->init != NULL) { ssa_build_stmt(proc, ms->init); } @@ -3030,6 +3114,113 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { proc->curr_block = done; case_end; + + case_ast_node(ms, TypeMatchStmt, node); + ssa_emit_comment(proc, make_string("TypeMatchStmt")); + gbAllocator allocator = proc->module->allocator; + + ssaValue *parent = ssa_build_expr(proc, ms->tag); + Type *union_type = type_deref(ssa_type(parent)); + GB_ASSERT(is_type_union(union_type)); + + ssaValue *tag_index = ssa_emit_struct_gep(proc, parent, v_zero32, make_type_pointer(allocator, t_int)); + tag_index = ssa_emit_load(proc, tag_index); + + i64 tag_size = proc->module->sizes.word_size; + i64 underlying_array_size = type_size_of(proc->module->sizes, allocator, union_type); + underlying_array_size -= tag_size; + Type *array_type = make_type_array(allocator, t_u8, underlying_array_size); + Type *array_type_ptr = make_type_pointer(allocator, array_type); + ssaValue *data = ssa_emit_struct_gep(proc, parent, v_one32, array_type_ptr); + data = ssa_array_elem(proc, data); + + ssaBlock *done = ssa__make_block(proc, node, make_string("type.match.done")); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + + String tag_var_name = ms->var->Ident.string; + + AstNode *default_stmts = NULL; + ssaBlock *default_block = NULL; + + isize case_count = body->list_count; + isize i = 0; + for (AstNode *clause = body->list; + clause != NULL; + clause = clause->next, i++) { + ssaBlock *body = NULL; + b32 append_body = false; + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + append_body = true; + if (cc->list == NULL) { + body = ssa__make_block(proc, clause, make_string("type.match.dflt.body")); + } else { + body = ssa__make_block(proc, clause, make_string("type.match.case.body")); + } + } + + + if (cc->list == NULL) { + // default case + default_stmts = cc->stmts; + default_block = body; + continue; + } + + Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause)); + Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); + ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); + ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type); + ssa_emit_store(proc, tag_var, data_ptr); + + + + Type *base_type = type_deref(tag_var_entity->type); + ssaValue *index = NULL; + for (isize i = 0; i < get_base_type(union_type)->Record.field_count; i++) { + Entity *f = get_base_type(union_type)->Record.fields[i]; + if (are_types_identical(f->type, base_type)) { + index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i)); + } + } + GB_ASSERT(index != NULL); + + ssaBlock *next_cond = ssa__make_block(proc, clause, make_string("type.match.case.next")); + Token eq = {Token_CmpEq}; + ssaValue *cond = ssa_emit_comp(proc, eq, tag_index, index); + ssa_emit_if(proc, cond, body, next_cond); + gb_array_append(proc->blocks, next_cond); + proc->curr_block = next_cond; + + if (append_body) { + gb_array_append(proc->blocks, body); + } + proc->curr_block = body; + ssa_push_target_list(proc, done, NULL, NULL); + ssa_build_stmt_list(proc, cc->stmts); + ssa_pop_target_list(proc); + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + gb_array_append(proc->blocks, default_block); + proc->curr_block = default_block; + ssa_push_target_list(proc, done, NULL, NULL); + ssa_build_stmt_list(proc, default_stmts); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + gb_array_append(proc->blocks, done); + proc->curr_block = done; + case_end; + case_ast_node(bs, BranchStmt, node); ssaBlock *block = NULL; switch (bs->token.kind) { @@ -3052,6 +3243,11 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { if (block != NULL && bs->token.kind != Token_fallthrough) { ssa_emit_defer_stmts(proc, ssaDefer_Branch, block); } + switch (bs->token.kind) { + case Token_break: ssa_emit_comment(proc, make_string("break")); break; + case Token_continue: ssa_emit_comment(proc, make_string("continue")); break; + case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break; + } ssa_emit_jump(proc, block); ssa_emit_unreachable(proc); case_end; diff --git a/src/parser.cpp b/src/parser.cpp index b2e4aba93..eb7d05952 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,6 +164,11 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \ AstNode *init, *tag; \ AstNode *body; \ }) \ + AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \ + Token token; \ + AstNode *tag, *var; \ + AstNode *body; \ + }) \ AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \ @@ -682,6 +687,16 @@ gb_inline AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNo return result; } + +gb_inline AstNode *make_type_match_stmt(AstFile *f, Token token, AstNode *tag, AstNode *var, AstNode *body) { + AstNode *result = make_node(f, AstNode_TypeMatchStmt); + result->TypeMatchStmt.token = token; + result->TypeMatchStmt.tag = tag; + result->TypeMatchStmt.var = var; + result->TypeMatchStmt.body = body; + return result; +} + gb_inline AstNode *make_case_clause(AstFile *f, Token token, AstNode *list, isize list_count, AstNode *stmts, isize stmt_count) { AstNode *result = make_node(f, AstNode_CaseClause); result->CaseClause.token = token; @@ -1399,10 +1414,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) { break; case Token_OpenBrace: { - if (is_literal_type(operand) && f->expr_level >= 0) { - if (lhs) { - // TODO(bill): Handle this - } + if (!lhs && is_literal_type(operand) && f->expr_level >= 0) { operand = parse_literal_value(f, operand); } else { loop = false; @@ -1432,7 +1444,7 @@ AstNode *parse_unary_expr(AstFile *f, b32 lhs) { AstNode *operand; Token op = f->cursor[0]; next_token(f); - operand = parse_unary_expr(f, false); + operand = parse_unary_expr(f, lhs); return make_unary_expr(f, op, operand); } break; } @@ -2247,6 +2259,23 @@ AstNode *parse_case_clause(AstFile *f) { return make_case_clause(f, token, list, list_count, stmts, stmt_count); } + +AstNode *parse_type_case_clause(AstFile *f) { + Token token = f->cursor[0]; + AstNode *clause = NULL; + if (allow_token(f, Token_case)) { + clause = parse_expr(f, false); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + isize stmt_count = 0; + AstNode *stmts = parse_stmt_list(f, &stmt_count); + + return make_case_clause(f, token, clause, 1, stmts, stmt_count); +} + + AstNode *parse_match_stmt(AstFile *f) { if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope"); @@ -2254,50 +2283,66 @@ AstNode *parse_match_stmt(AstFile *f) { } Token token = expect_token(f, Token_match); - AstNode *init = NULL; AstNode *tag = NULL; - if (f->cursor[0].kind != Token_OpenBrace) { - isize prev_level = f->expr_level; - f->expr_level = -1; - if (f->cursor[0].kind != Token_Semicolon) { - tag = parse_simple_stmt(f); + AstNode *body = NULL; + Token open, close; + + if (allow_token(f, Token_type)) { + tag = parse_expr(f, true); + expect_token(f, Token_ArrowRight); + AstNode *var = parse_identifier(f); + + open = expect_token(f, Token_OpenBrace); + AstNode *list = NULL; + AstNode *list_curr = NULL; + isize list_count = 0; + + while (f->cursor[0].kind == Token_case || + f->cursor[0].kind == Token_default) { + DLIST_APPEND(list, list_curr, parse_type_case_clause(f)); + list_count++; } - if (allow_token(f, Token_Semicolon)) { - init = tag; - tag = NULL; - if (f->cursor[0].kind != Token_OpenBrace) { + + close = expect_token(f, Token_CloseBrace); + body = make_block_stmt(f, list, list_count, open, close); + return make_type_match_stmt(f, token, tag, var, body); + } else { + if (f->cursor[0].kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->cursor[0].kind != Token_Semicolon) { tag = parse_simple_stmt(f); } + if (allow_token(f, Token_Semicolon)) { + init = tag; + tag = NULL; + if (f->cursor[0].kind != Token_OpenBrace) { + tag = parse_simple_stmt(f); + } + } + + f->expr_level = prev_level; } - f->expr_level = prev_level; + open = expect_token(f, Token_OpenBrace); + AstNode *list = NULL; + AstNode *list_curr = NULL; + isize list_count = 0; + + while (f->cursor[0].kind == Token_case || + f->cursor[0].kind == Token_default) { + DLIST_APPEND(list, list_curr, parse_case_clause(f)); + list_count++; + } + + close = expect_token(f, Token_CloseBrace); + + body = make_block_stmt(f, list, list_count, open, close); + + tag = convert_stmt_to_expr(f, tag, make_string("match expression")); + return make_match_stmt(f, token, init, tag, body); } - - Token open = expect_token(f, Token_OpenBrace); - AstNode *list = NULL; - AstNode *list_curr = NULL; - isize list_count = 0; -#if 1 - while (f->cursor[0].kind == Token_case || - f->cursor[0].kind == Token_default) { - DLIST_APPEND(list, list_curr, parse_case_clause(f)); - list_count++; - } -#else - - while (f->cursor[0].kind == Token_ArrowRight) { - DLIST_APPEND(list, list_curr, parse_case_clause(f)); - list_count++; - } - -#endif - Token close = expect_token(f, Token_CloseBrace); - - AstNode *body = make_block_stmt(f, list, list_count, open, close); - - tag = convert_stmt_to_expr(f, tag, make_string("match expression")); - return make_match_stmt(f, token, init, tag, body); }