diff --git a/code/demo.odin b/code/demo.odin index 78cee21d4..8c111eaa3 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,25 +1,7 @@ #import "fmt.odin" main :: proc() { - Entity :: union { - Apple: int - Banana: f32 - Goat: struct { - x, y: int - z, w: f32 - } - } - a := 123 as Entity.Apple - e: Entity = a - fmt.println(a) - - if apple, ok := ^e union_cast ^Entity.Apple; ok { - apple^ = 321 - e = apple^ - } - - apple, ok := e union_cast Entity.Apple - fmt.println(apple) } + diff --git a/code/game.odin b/code/game.odin index aebf4a931..a2f9ab7df 100644 --- a/code/game.odin +++ b/code/game.odin @@ -46,9 +46,12 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) w: Window w.width, w.height = msg, height - class_name := "Win32-Odin-Window\x00" - c_class_name := class_name.data - w.c_title = to_c_string(title) + c_class_name := "Win32-Odin-Window\x00".data + if title[title.count-1] != 0 { + w.c_title = to_c_string(title) + } else { + w.c_title = title as []u8 + } instance := GetModuleHandleA(nil) @@ -190,6 +193,7 @@ run :: proc() { draw_rect :: proc(x, y, w, h: f32) { gl.Begin(gl.TRIANGLES) + defer gl.End() gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0) gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0) @@ -198,8 +202,6 @@ run :: proc() { gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0) gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0) gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0) - - gl.End() } draw_rect(pos[0], pos[1], 50, 50) diff --git a/core/mem.odin b/core/mem.odin index 8a049daa9..f6c07e660 100644 --- a/core/mem.odin +++ b/core/mem.odin @@ -88,6 +88,7 @@ align_forward :: proc(ptr: rawptr, align: int) -> rawptr { } + AllocationHeader :: struct { size: int } @@ -109,18 +110,22 @@ allocation_header :: proc(data: rawptr) -> ^AllocationHeader { + + // Custom allocators Arena :: struct { backing: Allocator - memory: []u8 + memory: []byte temp_count: int + + Temp_Memory :: struct { + arena: ^Arena + original_count: int + } } -Temp_Arena_Memory :: struct { - arena: ^Arena - original_count: int -} + @@ -132,16 +137,10 @@ init_arena_from_memory :: proc(using a: ^Arena, data: []byte) { init_arena_from_context :: proc(using a: ^Arena, size: int) { backing = context.allocator - memory = new_slice(u8, 0, size) + memory = new_slice(byte, 0, size) temp_count = 0 } -init_sub_arena :: proc(sub, parent: ^Arena, size: int) { - push_allocator arena_allocator(parent) { - init_arena_from_context(sub, size) - } -} - free_arena :: proc(using a: ^Arena) { if backing.procedure != nil { push_allocator backing { @@ -181,7 +180,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode, case FREE: // NOTE(bill): Free all at once - // Use Temp_Arena_Memory if you want to free a block + // Use Arena.Temp_Memory if you want to free a block case FREE_ALL: arena.memory.count = 0 @@ -193,15 +192,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode, return nil } -begin_temp_arena_memory :: proc(a: ^Arena) -> Temp_Arena_Memory { - tmp: Temp_Arena_Memory +begin_arena_temp_memory :: proc(a: ^Arena) -> Arena.Temp_Memory { + tmp: Arena.Temp_Memory tmp.arena = a tmp.original_count = a.memory.count a.temp_count++ return tmp } -end_temp_arena_memory :: proc(using tmp: Temp_Arena_Memory) { +end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) { assert(arena.memory.count >= original_count) assert(arena.temp_count > 0) arena.memory.count = original_count @@ -232,7 +231,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int { case Pointer: return WORD_SIZE case Maybe: - return align_of_type_info(info.elem) + return max(align_of_type_info(info.elem), 1) case Procedure: return WORD_SIZE case Array: diff --git a/core/os.odin b/core/os.odin index f093d6a1a..4a98491c7 100644 --- a/core/os.odin +++ b/core/os.odin @@ -43,6 +43,7 @@ File_Standard :: type enum { COUNT, } +// NOTE(bill): Uses startup to initialize it __std_files := [..]File{ File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)}, File{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)}, diff --git a/core/utf8.odin b/core/utf8.odin index e604e069b..330db5b43 100644 --- a/core/utf8.odin +++ b/core/utf8.odin @@ -1,8 +1,11 @@ RUNE_ERROR :: #rune "\ufffd" RUNE_SELF :: 0x80 +RUNE_BOM :: 0xfeff +RUNE_EOF :: ~(0 as rune) MAX_RUNE :: #rune "\U0010ffff" UTF_MAX :: 4 + SURROGATE_MIN :: 0xd800 SURROGATE_MAX :: 0xdfff diff --git a/misc/debug.bat b/misc/debug.bat index 0d76416c2..eafd5cbd0 100644 --- a/misc/debug.bat +++ b/misc/debug.bat @@ -1,3 +1,3 @@ @echo off -call devenv.exe odin.sln +start devenv.exe odin.sln diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 1cbaf99d4..992b53f17 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -26,26 +26,23 @@ String const addressing_mode_strings[] = { struct Operand { AddressingMode mode; - Type *type; - ExactValue value; - AstNode *expr; - BuiltinProcId builtin_id; + Type * type; + ExactValue value; + AstNode * expr; + BuiltinProcId builtin_id; }; -b32 is_operand_nil(Operand *o) { - return o->mode == Addressing_Value && o->type == t_untyped_nil; -} struct TypeAndValue { AddressingMode mode; - Type *type; - ExactValue value; + Type * type; + ExactValue value; }; struct DeclInfo { Scope *scope; Entity **entities; - isize entity_count; + isize entity_count; AstNode *type_expr; AstNode *init_expr; @@ -55,13 +52,11 @@ struct DeclInfo { Map deps; // Key: Entity * }; - - struct ExpressionInfo { - b32 is_lhs; // Debug info + b32 is_lhs; // Debug info AddressingMode mode; - Type *type; // Type_Basic - ExactValue value; + Type * type; // Type_Basic + ExactValue value; }; ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, ExactValue value) { @@ -213,10 +208,9 @@ struct CheckerInfo { Map untyped; // Key: AstNode * | Expression -> ExpressionInfo Map entities; // Key: Entity * Map foreign_procs; // Key: String + Map files; // Key: String (full path) Map type_info_map; // Key: Type * - Map files; // Key: String isize type_info_index; - Entity * implicit_values[ImplicitValue_Count]; }; @@ -282,12 +276,14 @@ void destroy_declaration_info(DeclInfo *d) { } b32 decl_info_has_init(DeclInfo *d) { - if (d->init_expr != NULL) + if (d->init_expr != NULL) { return true; + } if (d->proc_decl != NULL) { ast_node(pd, ProcDecl, d->proc_decl); - if (pd->body != NULL) + if (pd->body != NULL) { return true; + } } return false; @@ -629,12 +625,14 @@ Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) { Type *type_of_expr(CheckerInfo *i, AstNode *expression) { TypeAndValue *found = type_and_value_of_expression(i, expression); - if (found) + if (found) { return found->type; + } if (expression->kind == AstNode_Ident) { Entity *entity = entity_of_ident(i, expression); - if (entity) + if (entity) { return entity->type; + } } return NULL; @@ -647,8 +645,9 @@ void add_untyped(CheckerInfo *i, AstNode *expression, b32 lhs, AddressingMode mo void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) { GB_ASSERT(expression != NULL); - if (mode == Addressing_Invalid) + if (mode == Addressing_Invalid) { return; + } if (mode == Addressing_Constant) { if (is_type_constant_type(type)) { @@ -1145,6 +1144,12 @@ void check_parsed_files(Checker *c) { auto found = map_get(&file_scopes, key); GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath)); Scope *scope = *found; + + if (scope->is_global) { + error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary"); + continue; + } + b32 previously_added = false; for_array(import_index, file_scope->imported) { Scope *prev = file_scope->imported[import_index]; @@ -1153,6 +1158,7 @@ void check_parsed_files(Checker *c) { break; } } + if (!previously_added) { array_add(&file_scope->imported, scope); } else { diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 0df216ce8..ac2c443c0 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -106,6 +106,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type); entity->using_parent = parent; entity->Variable.anonymous = true; + entity->Variable.anonymous = true; return entity; } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index bb3a6db2e..7f7f9a5f7 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -79,7 +79,7 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu break; } if (type_has_nil(dst)) { - return is_operand_nil(operand); + return operand->mode == Addressing_Value && operand->type == t_untyped_nil; } } @@ -156,8 +156,9 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n PROF_PROC(); check_not_tuple(c, operand); - if (operand->mode == Addressing_Invalid) + if (operand->mode == Addressing_Invalid) { return; + } if (is_type_untyped(operand->type)) { Type *target_type = type; @@ -1379,10 +1380,12 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac return true; } } else if (is_type_pointer(type)) { - if (in_value.kind == ExactValue_Pointer) + if (in_value.kind == ExactValue_Pointer) { return true; - if (in_value.kind == ExactValue_Integer) + } + if (in_value.kind == ExactValue_Integer) { return true; + } if (out_value) *out_value = in_value; } @@ -1492,8 +1495,9 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) { o->value = exact_unary_operator_value(op, o->value, precision); if (is_type_typed(type)) { - if (node != NULL) + if (node != NULL) { o->expr = node; + } check_is_expressible(c, o, type); } return; @@ -1509,9 +1513,8 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { defer (gb_temp_arena_memory_end(tmp)); gbString err_str = NULL; - defer ({ - if (err_str != NULL) - gb_string_free(err_str); + defer (if (err_str != NULL) { + gb_string_free(err_str); }); if (check_is_assignable_to(c, x, y->type) || @@ -1520,13 +1523,13 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { switch (op.kind) { case Token_CmpEq: case Token_NotEq: - defined = is_type_comparable(base_type(x->type)); + defined = is_type_comparable(get_enum_base_type(base_type(x->type))); break; case Token_Lt: case Token_Gt: case Token_LtEq: case Token_GtEq: { - defined = is_type_ordered(base_type(x->type)); + defined = is_type_ordered(get_enum_base_type(base_type(x->type))); } break; } @@ -1546,7 +1549,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { } if (err_str != NULL) { - error(op, "Cannot compare expression, %s", err_str); + error(ast_node_token(x->expr), "Cannot compare expression, %s", err_str); x->type = t_untyped_bool; return; } @@ -1682,27 +1685,29 @@ b32 check_is_castable_to(Checker *c, Operand *operand, Type *y) { // Cast between booleans and integers if (is_type_boolean(xb) || is_type_integer(xb)) { - if (is_type_boolean(yb) || is_type_integer(yb)) + if (is_type_boolean(yb) || is_type_integer(yb)) { return true; + } } // Cast between numbers if (is_type_integer(xb) || is_type_float(xb)) { - if (is_type_integer(yb) || is_type_float(yb)) + if (is_type_integer(yb) || is_type_float(yb)) { return true; + } } // Cast between pointers if (is_type_pointer(xb) && is_type_pointer(yb)) { - return true; + return true; } // (u)int <-> pointer if (is_type_int_or_uint(xb) && is_type_rawptr(yb)) { - return true; + return true; } if (is_type_rawptr(xb) && is_type_int_or_uint(yb)) { - return true; + return true; } // []byte/[]u8 <-> string @@ -1747,8 +1752,9 @@ String check_down_cast_name(Type *dst_, Type *src_) { if (!is_type_pointer(f->type)) { result = check_down_cast_name(f->type, src_); - if (result.len > 0) + if (result.len > 0) { return result; + } } } } @@ -1808,8 +1814,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { if (be->op.kind == Token_as) { check_expr(c, x, be->left); Type *type = check_type(c, be->right); - if (x->mode == Addressing_Invalid) + if (x->mode == Addressing_Invalid) { return; + } b32 is_const_expr = x->mode == Addressing_Constant; b32 can_convert = false; @@ -1854,8 +1861,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { } else if (be->op.kind == Token_transmute) { check_expr(c, x, be->left); Type *type = check_type(c, be->right); - if (x->mode == Addressing_Invalid) + if (x->mode == Addressing_Invalid) { return; + } if (x->mode == Addressing_Constant) { gbString expr_str = expr_to_string(x->expr); @@ -2160,8 +2168,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { } x->value = exact_binary_operator_value(op, a, b); if (is_type_typed(type)) { - if (node != NULL) + if (node != NULL) { x->expr = node; + } check_is_expressible(c, x, type); } return; @@ -2176,19 +2185,22 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) { HashKey key = hash_pointer(e); ExpressionInfo *found = map_get(&c->info.untyped, key); - if (found == NULL) + if (found == NULL) { return; + } switch (e->kind) { case_ast_node(ue, UnaryExpr, e); - if (found->value.kind != ExactValue_Invalid) + if (found->value.kind != ExactValue_Invalid) { break; + } update_expr_type(c, ue->expr, type, final); case_end; case_ast_node(be, BinaryExpr, e); - if (found->value.kind != ExactValue_Invalid) + if (found->value.kind != ExactValue_Invalid) { break; + } if (!token_is_comparison(be->op)) { if (token_is_shift(be->op)) { update_expr_type(c, be->left, type, final); @@ -2222,8 +2234,9 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) { void update_expr_value(Checker *c, AstNode *e, ExactValue value) { ExpressionInfo *found = map_get(&c->info.untyped, hash_pointer(e)); - if (found) + if (found) { found->value = value; + } } void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) { @@ -2389,10 +2402,10 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { ast_node(se, SelectorExpr, node); b32 check_op_expr = true; + Entity *expr_entity = NULL; Entity *entity = NULL; Selection sel = {}; // NOTE(bill): Not used if it's an import name - AstNode *op_expr = se->expr; AstNode *selector = unparen_expr(se->selector); if (selector == NULL) { @@ -2401,10 +2414,12 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { GB_ASSERT(selector->kind == AstNode_Ident); + if (op_expr->kind == AstNode_Ident) { String name = op_expr->Ident.string; Entity *e = scope_lookup_entity(c->context.scope, name); add_entity_use(c, op_expr, e); + expr_entity = e; if (e != NULL && e->kind == Entity_ImportName) { String sel_name = selector->Ident.string; check_op_expr = false; @@ -2459,6 +2474,17 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { goto error; } + if (expr_entity != NULL && expr_entity->kind == Entity_Constant && entity->kind != Entity_Constant) { + gbString op_str = expr_to_string(op_expr); + gbString type_str = type_to_string(operand->type); + gbString sel_str = expr_to_string(selector); + defer (gb_string_free(op_str)); + defer (gb_string_free(type_str)); + defer (gb_string_free(sel_str)); + error(ast_node_token(op_expr), "Cannot access non-constant field `%s` from `%s`", sel_str, op_str); + goto error; + } + add_entity_use(c, selector, entity); @@ -2570,8 +2596,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } check_expr(c, &op, len); - if (op.mode == Addressing_Invalid) + if (op.mode == Addressing_Invalid) { return false; + } if (!is_type_integer(op.type)) { gbString type_str = type_to_string(operand->type); defer (gb_string_free(type_str)); @@ -2583,8 +2610,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (cap != NULL) { check_expr(c, &op, cap); - if (op.mode == Addressing_Invalid) + if (op.mode == Addressing_Invalid) { return false; + } if (!is_type_integer(op.type)) { gbString type_str = type_to_string(operand->type); defer (gb_string_free(type_str)); @@ -2709,8 +2737,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) ast_node(s, SelectorExpr, arg); check_expr(c, operand, s->expr); - if (operand->mode == Addressing_Invalid) + if (operand->mode == Addressing_Invalid) { return false; + } Type *type = operand->type; if (base_type(type)->kind == Type_Pointer) { @@ -2750,8 +2779,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) case BuiltinProc_type_of_val: // type_of_val :: proc(val: Type) -> type(Type) check_assignment(c, operand, NULL, make_string("argument of `type_of_val`")); - if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) + if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) { return false; + } operand->mode = Addressing_Type; break; @@ -3140,8 +3170,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Operand a = *operand; Operand b = {}; check_expr(c, &b, other_arg); - if (b.mode == Addressing_Invalid) + if (b.mode == Addressing_Invalid) { return false; + } if (!is_type_comparable(b.type) || !is_type_numeric(type)) { gbString type_str = type_to_string(b.type); defer (gb_string_free(type_str)); @@ -3151,7 +3182,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) return false; } - if (a.mode == Addressing_Constant && b.mode == Addressing_Constant) { ExactValue x = a.value; @@ -3170,6 +3200,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; operand->type = type; + convert_to_typed(c, &a, b.type); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(operand->type, b.type)) { gbString type_a = type_to_string(a.type); gbString type_b = type_to_string(b.type); @@ -3200,8 +3239,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Operand a = *operand; Operand b = {}; check_expr(c, &b, other_arg); - if (b.mode == Addressing_Invalid) + if (b.mode == Addressing_Invalid) { return false; + } if (!is_type_comparable(b.type) || !is_type_numeric(type)) { gbString type_str = type_to_string(b.type); defer (gb_string_free(type_str)); @@ -3211,7 +3251,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) return false; } - if (a.mode == Addressing_Constant && b.mode == Addressing_Constant) { ExactValue x = a.value; @@ -3230,6 +3269,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; operand->type = type; + convert_to_typed(c, &a, b.type); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(operand->type, b.type)) { gbString type_a = type_to_string(a.type); gbString type_b = type_to_string(b.type); @@ -3847,6 +3895,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } Type *t = base_type(type_deref(o->type)); + b32 is_const = o->mode == Addressing_Constant; + auto set_index_data = [](Operand *o, Type *t, i64 *max_count) -> b32 { t = base_type(type_deref(t)); @@ -3894,6 +3944,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint i64 max_count = -1; b32 valid = set_index_data(o, t, &max_count); + if (is_const) { + valid = false; + } + if (!valid && (is_type_struct(t) || is_type_raw_union(t))) { Entity *found = find_using_index_expr(t); if (found != NULL) { @@ -3903,7 +3957,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint if (!valid) { gbString str = expr_to_string(o->expr); - error(ast_node_token(o->expr), "Cannot index `%s`", str); + if (is_const) { + error(ast_node_token(o->expr), "Cannot index a constant `%s`", str); + } else { + error(ast_node_token(o->expr), "Cannot index `%s`", str); + } gb_string_free(str); goto error; } diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 0b703edac..027c072a0 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -983,7 +983,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { isize rhs_count = operands.count; isize operand_index = 0; - for_array(i, as->lhs) { + for_array(i, operands) { AstNode *lhs = as->lhs[i]; check_assignment_variable(c, &operands[i], lhs); } @@ -1400,16 +1400,19 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { Token token = bs->token; switch (token.kind) { case Token_break: - if ((flags & Stmt_BreakAllowed) == 0) + if ((flags & Stmt_BreakAllowed) == 0) { error(token, "`break` only allowed in `for` or `match` statements"); + } break; case Token_continue: - if ((flags & Stmt_ContinueAllowed) == 0) + if ((flags & Stmt_ContinueAllowed) == 0) { error(token, "`continue` only allowed in `for` statements"); + } break; case Token_fallthrough: - if ((flags & Stmt_FallthroughAllowed) == 0) + if ((flags & Stmt_FallthroughAllowed) == 0) { error(token, "`fallthrough` statement in illegal position"); + } break; default: error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); @@ -1432,7 +1435,6 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { e = scope_lookup_entity(c->context.scope, name); } else if (expr->kind == AstNode_SelectorExpr) { Operand o = {}; - check_expr_base(c, &o, expr->SelectorExpr.expr); e = check_selector(c, &o, expr); is_selector = true; } @@ -1499,15 +1501,6 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } } break; - case Entity_Constant: - error(us->token, "`using` cannot be applied to a constant"); - break; - - case Entity_Procedure: - case Entity_Builtin: - error(us->token, "`using` cannot be applied to a procedure"); - break; - case Entity_Variable: { Type *t = base_type(type_deref(e->type)); if (is_type_struct(t) || is_type_raw_union(t)) { @@ -1533,8 +1526,29 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } } break; + case Entity_Constant: + error(us->token, "`using` cannot be applied to a constant"); + break; + + case Entity_Procedure: + case Entity_Builtin: + error(us->token, "`using` cannot be applied to a procedure"); + break; + + case Entity_ImplicitValue: + error(us->token, "`using` cannot be applied to an implicit value"); + break; + + case Entity_Nil: + error(us->token, "`using` cannot be applied to `nil`"); + break; + + case Entity_Invalid: + error(us->token, "`using` cannot be applied to an invalid entity"); + break; + default: - GB_PANIC("TODO(bill): using other expressions?"); + GB_PANIC("TODO(bill): `using` other expressions?"); } case_end; diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 7d84a6c7b..c06b6d527 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -7,8 +7,9 @@ struct ssaGen { }; b32 ssa_gen_init(ssaGen *s, Checker *c) { - if (global_error_collector.count != 0) + if (global_error_collector.count != 0) { return false; + } isize tc = c->parser->total_token_count; if (tc < 2) { @@ -43,7 +44,7 @@ String ssa_mangle_name(ssaGen *s, String path, String name) { AstFile *file = *map_get(&info->files, hash_string(path)); char *str = gb_alloc_array(a, char, path.len+1); - gb_memcopy(str, path.text, path.len); + gb_memmove(str, path.text, path.len); str[path.len] = 0; for (isize i = 0; i < path.len; i++) { if (str[i] == '\\') { diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 95fd4fe5b..abfd61f44 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -31,7 +31,7 @@ void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { f->offset = 0; } u8 *cursor = cast(u8 *)f->vm.data + f->offset; - gb_memcopy(cursor, data, len); + gb_memmove(cursor, data, len); f->offset += len; } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index b93d42d21..ed4899251 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -21,14 +21,14 @@ struct ssaDebugInfo { union { struct { - AstFile *file; - String producer; + AstFile * file; + String producer; ssaDebugInfo *all_procs; } CompileUnit; struct { AstFile *file; - String filename; - String directory; + String filename; + String directory; } File; struct { Entity * entity; @@ -111,8 +111,8 @@ enum ssaDeferKind { struct ssaDefer { ssaDeferKind kind; - isize scope_index; - ssaBlock *block; + isize scope_index; + ssaBlock * block; union { AstNode *stmt; // NOTE(bill): `instr` will be copied every time to create a new one @@ -121,7 +121,7 @@ struct ssaDefer { }; struct ssaProcedure { - ssaProcedure *parent; + ssaProcedure * parent; Array children; Entity * entity; @@ -1422,7 +1422,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) { proc->curr_block = proc->decl_block; ssa_emit_jump(proc, proc->entry_block); -#if 1 +#if 0 ssa_optimize_blocks(proc); ssa_build_referrers(proc); ssa_build_dom_tree(proc); @@ -2243,7 +2243,6 @@ ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) } GB_ASSERT(dst_tag != NULL); - ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); @@ -3647,10 +3646,10 @@ void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) { child.text = gb_alloc_array(m->allocator, u8, len); isize i = 0; - gb_memcopy(child.text+i, parent.text, parent.len); + gb_memmove(child.text+i, parent.text, parent.len); i += parent.len; child.text[i++] = '.'; - gb_memcopy(child.text+i, cn.text, cn.len); + gb_memmove(child.text+i, cn.text, cn.len); map_set(&m->type_names, hash_pointer(field->type), child); ssa_gen_global_type_name(m, field, child); @@ -4301,8 +4300,6 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { } break; } - // TODO(bill): Handle fallthrough scope exit correctly - // if (block != NULL && bs->token.kind != Token_fallthrough) { if (block != NULL) { ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block); } @@ -4695,7 +4692,7 @@ void ssa_build_dom_tree(ssaProcedure *proc) { // Step 1 - number vertices i32 pre_num = ssa_lt_depth_first_search(<, root, 0, preorder); - gb_memcopy(buckets, preorder, n*gb_size_of(preorder[0])); + gb_memmove(buckets, preorder, n*gb_size_of(preorder[0])); for (i32 i = n-1; i > 0; i--) { ssaBlock *w = preorder[i]; diff --git a/src/common.cpp b/src/common.cpp index dc66efb1f..4399e3962 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,4 +1,3 @@ -// #define GB_NO_WINDOWS_H #define GB_IMPLEMENTATION #include "gb/gb.h" @@ -36,9 +35,8 @@ String get_module_dir() { wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1); - String16 str = {text, len}; GetModuleFileNameW(NULL, text, len); - String path = string16_to_string(gb_heap_allocator(), str); + String path = string16_to_string(gb_heap_allocator(), make_string16(text, len)); for (isize i = path.len-1; i >= 0; i--) { u8 c = path.text[i]; if (c == '/' || c == '\\') { @@ -85,18 +83,22 @@ struct BlockTimer { // Hasing +enum HashKeyKind { + HashKey_Default, + HashKey_String, +}; struct HashKey { + HashKeyKind kind; + u64 key; union { - u64 key; - void *ptr; + String string; // if String, s.len > 0 }; - b32 is_string; - String string; // if String, s.len > 0 }; gb_inline HashKey hashing_proc(void const *data, isize len) { HashKey h = {}; + h.kind = HashKey_Default; // h.key = gb_murmur64(data, len); h.key = gb_fnv64a(data, len); return h; @@ -104,22 +106,24 @@ gb_inline HashKey hashing_proc(void const *data, isize len) { gb_inline HashKey hash_string(String s) { HashKey h = hashing_proc(s.text, s.len); - h.is_string = true; + h.kind = HashKey_String; h.string = s; return h; } gb_inline HashKey hash_pointer(void *ptr) { - uintptr p = cast(uintptr)ptr; - HashKey h = {cast(u64)p}; + HashKey h = {}; + h.key = cast(u64)cast(uintptr)ptr; return h; } b32 hash_key_equal(HashKey a, HashKey b) { if (a.key == b.key) { // NOTE(bill): If two string's hashes collide, compare the strings themselves - if (a.is_string) { - if (b.is_string) return a.string == b.string; + if (a.kind == HashKey_String) { + if (b.kind == HashKey_String) { + return a.string == b.string; + } return false; } return true; @@ -156,7 +160,6 @@ i64 prev_pow2(i64 n) { } -#define gb_for_array(index_, array_) for (isize index_ = 0; (array_) != NULL && index_ < gb_array_count(array_); index_++) #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) @@ -169,10 +172,11 @@ i64 prev_pow2(i64 n) { } while (0) #define DLIST_APPEND(root_element, curr_element, next_element) do { \ - if ((root_element) == NULL) \ + if ((root_element) == NULL) { \ (root_element) = (curr_element) = (next_element); \ - else \ + } else { \ DLIST_SET(curr_element, next_element); \ + } \ } while (0) //////////////////////////////////////////////////////////////// @@ -309,14 +313,16 @@ void map_rehash(Map *h, isize new_count) { } fr = map__find(&nh, e->key); j = map__add_entry(&nh, e->key); - if (fr.entry_prev < 0) + if (fr.entry_prev < 0) { nh.hashes[fr.hash_index] = j; - else + } else { nh.entries[fr.entry_prev].next = j; + } nh.entries[j].next = fr.entry_index; nh.entries[j].value = e->value; - if (map__full(&nh)) + if (map__full(&nh)) { map_grow(&nh); + } } map_destroy(h); *h = nh; @@ -467,3 +473,6 @@ void multi_map_remove_all(Map *h, HashKey key) { } } + + + diff --git a/src/exact_value.cpp b/src/exact_value.cpp index c83dc2d75..15d03ba2b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -12,8 +12,8 @@ enum ExactValueKind { ExactValue_String, ExactValue_Integer, ExactValue_Float, - ExactValue_Pointer, // TODO(bill): Handle ExactValue_Pointer correctly - ExactValue_Compound, + ExactValue_Pointer, + ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Count, }; diff --git a/src/main.cpp b/src/main.cpp index 661c4dcfe..8526234c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#define VERSION_STRING "v0.0.3" + #include "common.cpp" #include "profiler.cpp" #include "unicode.cpp" @@ -88,9 +90,20 @@ ArchData make_arch_data(ArchKind kind) { return data; } +void usage(char *argv0) { + gb_printf_err("%s is a tool for managing Odin source code\n", argv0); + gb_printf_err("Usage:"); + gb_printf_err("\n\t%s command [arguments]\n", argv0); + gb_printf_err("Commands:"); + gb_printf_err("\n\tbuild compile .odin file"); + gb_printf_err("\n\trun compile and run .odin file"); + gb_printf_err("\n\tversion print Odin version"); + gb_printf_err("\n\n"); +} + int main(int argc, char **argv) { if (argc < 2) { - gb_printf_err("using: %s [run] \n", argv[0]); + usage(argv[0]); return 1; } prof_init(); @@ -104,11 +117,20 @@ int main(int argc, char **argv) { init_universal_scope(); - char *init_filename = argv[1]; + char *init_filename = NULL; b32 run_output = false; - if (gb_strncmp(argv[1], "run", 3) == 0) { + String arg1 = make_string(argv[1]); + if (arg1 == "run") { run_output = true; init_filename = argv[2]; + } else if (arg1 == "build") { + init_filename = argv[2]; + } else if (arg1 == "version") { + gb_printf("%s version %s", argv[0], VERSION_STRING); + return 0; + } else { + usage(argv[0]); + return 1; } Parser parser = {0}; diff --git a/src/parser.cpp b/src/parser.cpp index 6e6d44484..663bec46d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -29,37 +29,36 @@ struct AstFile { // >= 0: In Expression // < 0: In Control Clause // NOTE(bill): Used to prevent type literals in control clauses - isize expr_level; + isize expr_level; - AstNodeArray decls; - b32 is_global_scope; + AstNodeArray decls; + b32 is_global_scope; - AstNode * curr_proc; - isize scope_level; - Scope * scope; // NOTE(bill): Created in checker - DeclInfo *decl_info; // NOTE(bill): Created in checker + AstNode * curr_proc; + isize scope_level; + Scope * scope; // NOTE(bill): Created in checker + DeclInfo * decl_info; // NOTE(bill): Created in checker // TODO(bill): Error recovery - // NOTE(bill): Error recovery #define PARSER_MAX_FIX_COUNT 6 isize fix_count; TokenPos fix_prev_pos; }; struct ImportedFile { - String path; - String rel_path; + String path; + String rel_path; TokenPos pos; // #import }; struct Parser { - String init_fullpath; + String init_fullpath; Array files; Array imports; - gbAtomic32 import_index; + gbAtomic32 import_index; Array system_libraries; - isize total_token_count; - gbMutex mutex; + isize total_token_count; + gbMutex mutex; }; enum ProcTag : u64 { @@ -483,11 +482,6 @@ Token ast_node_token(AstNode *node) { return empty_token; } -HashKey hash_token(Token t) { - return hash_string(t.string); -} - - // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ AstNode *make_node(AstFile *f, AstNodeKind kind) { gbArena *arena = &f->arena; @@ -2969,8 +2963,8 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) { defer (gb_free(gb_heap_allocator(), str)); isize i = 0; - gb_memcopy(str+i, base_dir.text, base_dir.len); i += base_dir.len; - gb_memcopy(str+i, path.text, path.len); + gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len; + gb_memmove(str+i, path.text, path.len); str[str_len] = '\0'; return path_to_fullpath(a, make_string(str, str_len)); } @@ -2985,9 +2979,9 @@ String get_fullpath_core(gbAllocator a, String path) { u8 *str = gb_alloc_array(gb_heap_allocator(), u8, str_len+1); defer (gb_free(gb_heap_allocator(), str)); - gb_memcopy(str, module_dir.text, module_dir.len); - gb_memcopy(str+module_dir.len, core, core_len); - gb_memcopy(str+module_dir.len+core_len, path.text, path.len); + gb_memmove(str, module_dir.text, module_dir.len); + gb_memmove(str+module_dir.len, core, core_len); + gb_memmove(str+module_dir.len+core_len, path.text, path.len); str[str_len] = '\0'; return path_to_fullpath(a, make_string(str, str_len)); diff --git a/src/string.cpp b/src/string.cpp index a8e8b5583..7d94f9636 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -1,7 +1,7 @@ gb_global gbArena string_buffer_arena = {}; gb_global gbAllocator string_buffer_allocator = {}; -void init_string_buffer_memory() { +void init_string_buffer_memory(void) { // NOTE(bill): This should be enough memory for file systems gb_arena_init_from_allocator(&string_buffer_arena, gb_heap_allocator(), gb_megabytes(1)); string_buffer_allocator = gb_arena_allocator(&string_buffer_arena); @@ -404,7 +404,7 @@ i32 unquote_string(gbAllocator a, String *s_) { buf[offset++] = cast(u8)r; } else { isize size = gb_utf8_encode_rune(rune_temp, r); - gb_memcopy(buf+offset, rune_temp, size); + gb_memmove(buf+offset, rune_temp, size); offset += size; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 0dfab6fc6..a7f53c0f2 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -158,8 +158,7 @@ Token empty_token = {Token_Invalid}; Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}}; Token make_token_ident(String s) { - Token t = {Token_Identifier}; - t.string = s; + Token t = {Token_Identifier, s}; return t; } @@ -356,6 +355,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { defer (gb_free(gb_heap_allocator(), c_str)); + // TODO(bill): Memory map rather than copy contents gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str); gb_zero_item(t); if (fc.data != NULL) { @@ -368,8 +368,9 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { t->line_count = 1; advance_to_next_rune(t); - if (t->curr_rune == GB_RUNE_BOM) + if (t->curr_rune == GB_RUNE_BOM) { advance_to_next_rune(t); // Ignore BOM at file beginning + } array_init(&t->allocated_strings, gb_heap_allocator()); @@ -389,9 +390,9 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { return TokenizerInit_Permission; } - if (gb_file_size(&f) == 0) + if (gb_file_size(&f) == 0) { return TokenizerInit_Empty; - + } return TokenizerInit_None; } @@ -413,12 +414,13 @@ void tokenizer_skip_whitespace(Tokenizer *t) { } gb_inline i32 digit_value(Rune r) { - if (gb_char_is_digit(cast(char)r)) + if (gb_char_is_digit(cast(char)r)) { return r - '0'; - if (gb_is_between(cast(char)r, 'a', 'f')) + } else if (gb_is_between(cast(char)r, 'a', 'f')) { return r - 'a' + 10; - if (gb_is_between(cast(char)r, 'A', 'F')) + } else if (gb_is_between(cast(char)r, 'A', 'F')) { return r - 'A' + 10; + } return 16; // NOTE(bill): Larger than highest possible } @@ -426,22 +428,21 @@ gb_inline void scan_mantissa(Tokenizer *t, i32 base) { // TODO(bill): Allow for underscores in numbers as a number separator // TODO(bill): Is this a good idea? // while (digit_value(t->curr_rune) < base || t->curr_rune == '_') - while (digit_value(t->curr_rune) < base) + while (digit_value(t->curr_rune) < base) { advance_to_next_rune(t); + } } Token scan_number_to_token(Tokenizer *t, b32 seen_decimal_point) { Token token = {}; - u8 *start_curr = t->curr; token.kind = Token_Integer; - token.string = make_string(start_curr, 1); + token.string = make_string(t->curr, 1); token.pos.file = t->fullpath; token.pos.line = t->line_count; token.pos.column = t->curr-t->line+1; if (seen_decimal_point) { - start_curr--; token.kind = Token_Float; scan_mantissa(t, 10); goto exponent; @@ -497,8 +498,9 @@ exponent: if (t->curr_rune == 'e' || t->curr_rune == 'E') { token.kind = Token_Float; advance_to_next_rune(t); - if (t->curr_rune == '-' || t->curr_rune == '+') + if (t->curr_rune == '-' || t->curr_rune == '+') { advance_to_next_rune(t); + } scan_mantissa(t, 10); } @@ -623,8 +625,9 @@ Token tokenizer_get_token(Tokenizer *t) { curr_rune = t->curr_rune; if (rune_is_letter(curr_rune)) { token.kind = Token_Identifier; - while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) + while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) { advance_to_next_rune(t); + } token.string.len = t->curr - token.string.text; diff --git a/src/unicode.cpp b/src/unicode.cpp index a9781d8f7..872881414 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -6,7 +6,6 @@ #pragma warning(pop) -// TODO(bill): Unicode support b32 rune_is_letter(Rune r) { if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) || r == '_') { @@ -24,8 +23,9 @@ b32 rune_is_letter(Rune r) { } b32 rune_is_digit(Rune r) { - if (r < 0x80 && gb_is_between(r, '0', '9')) + if (r < 0x80 && gb_is_between(r, '0', '9')) { return true; + } return utf8proc_category(r) == UTF8PROC_CATEGORY_ND; }