diff --git a/code/demo.odin b/code/demo.odin index 9a4d522cc..ec54c491c 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,15 +1,6 @@ #import "fmt.odin" main :: proc() { - Vec3 :: struct { - x, y: i16 - z: ?i32 - } - a := [..]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - offset: u8 = 2 - ptr := ^a[4] - - fmt.println((ptr+offset) - ptr) } diff --git a/core/fmt.odin b/core/fmt.odin index c9c8d85e4..cb147228a 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -406,8 +406,9 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { case Array: - print_string_to_buffer(buf, "[") - defer print_string_to_buffer(buf, "]") + bprintf(buf, "[%]%{", info.count, info.elem) + defer print_string_to_buffer(buf, "}") + for i := 0; i < info.count; i++ { if i > 0 { @@ -420,8 +421,8 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { case Slice: slice := arg.data as ^[]byte - print_string_to_buffer(buf, "[") - defer print_string_to_buffer(buf, "]") + bprintf(buf, "[]%{", info.elem) + defer print_string_to_buffer(buf, "}") for i := 0; i < slice.count; i++ { if i > 0 { @@ -443,8 +444,8 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { return false } - print_string_to_buffer(buf, "<") - defer print_string_to_buffer(buf, ">") + bprintf(buf, "{%}%{", info.count, info.elem) + defer print_string_to_buffer(buf, "}") if is_bool(info.elem) { return @@ -461,8 +462,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { case Struct: - print_string_to_buffer(buf, "struct") - print_string_to_buffer(buf, "{") + bprintf(buf, "%{", arg.type_info) defer print_string_to_buffer(buf, "}") for i := 0; i < info.fields.count; i++ { diff --git a/core/hash.odin b/core/hash.odin index 2f15f28b3..b0b979349 100644 --- a/core/hash.odin +++ b/core/hash.odin @@ -138,7 +138,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 { len -= 4 } - data8 := slice_ptr(ptr_offset(data.data, i) as ^u8, 3) // NOTE(bill): This is unsafe + data8 := slice_ptr((data.data+i) as ^u8, 3) // NOTE(bill): This is unsafe match len { case 3: h2 ~= data8[2] as u32 << 16; fallthrough diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index f044b6f36..18f5548a7 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -181,6 +181,23 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("enum_to_string"), 1, false, Expr_Expr}, }; +enum ImplicitValueId { + ImplicitValue_Invalid, + + ImplicitValue_context, + + ImplicitValue_Count, +}; +struct ImplicitValueInfo { + String name; + String backing_name; + Type * type; +}; +// NOTE(bill): This is initialized later +gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {}; + + + struct CheckerContext { Scope *scope; DeclInfo *decl; @@ -199,6 +216,8 @@ struct CheckerInfo { Map type_info_map; // Key: Type * Map files; // Key: String isize type_info_index; + + Entity * implicit_values[ImplicitValue_Count]; }; struct Checker { @@ -423,8 +442,9 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) { String name = entity->token.string; HashKey key = hash_string(name); Entity **found = map_get(&s->elements, key); - if (found) + if (found) { return *found; + } map_set(&s->elements, key, entity); if (entity->scope == NULL) { entity->scope = s; @@ -950,8 +970,17 @@ void init_preload_types(Checker *c) { t_context = e->type; t_context_ptr = make_type_pointer(c->allocator, t_context); - add_global_entity(make_entity_implicit_value(gb_heap_allocator(), make_string("context"), t_context)); } + +} + +void add_implicit_value(Checker *c, ImplicitValueId id, String name, String backing_name, Type *type) { + ImplicitValueInfo info = {name, backing_name, type}; + Entity *value = make_entity_implicit_value(c->allocator, info.name, info.type, id); + Entity *prev = scope_insert_entity(c->global_scope, value); + GB_ASSERT(prev == NULL); + implicit_value_infos[id] = info; + c->info.implicit_values[id] = value; } void check_parsed_files(Checker *c) { @@ -1228,11 +1257,24 @@ void check_parsed_files(Checker *c) { check_global_entity(c, Entity_TypeName); init_preload_types(c); + add_implicit_value(c, ImplicitValue_context, make_string("context"), make_string("__context"), t_context); check_global_entity(c, Entity_Constant); check_global_entity(c, Entity_Procedure); check_global_entity(c, Entity_Variable); + for (isize i = 1; i < ImplicitValue_Count; i++) { + // NOTE(bill): First is invalid + Entity *e = c->info.implicit_values[i]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + + ImplicitValueInfo *ivi = &implicit_value_infos[i]; + Entity *backing = scope_lookup_entity(e->scope, ivi->backing_name); + GB_ASSERT(backing != NULL); + e->ImplicitValue.backing = backing; + } + + // Check procedure bodies for_array(i, c->procs) { ProcedureInfo *pi = &c->procs[i]; diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 8b5578fff..0df216ce8 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -2,6 +2,7 @@ struct Scope; struct Checker; struct Type; enum BuiltinProcId; +enum ImplicitValueId; #define ENTITY_KINDS \ ENTITY_KIND(Invalid), \ @@ -63,10 +64,13 @@ struct Entity { String path; String name; Scope *scope; - b32 used; + b32 used; } ImportName; struct {} Nil; - struct {} ImplicitValue; + struct { + ImplicitValueId id; + Entity * backing; + } ImplicitValue; }; }; @@ -160,9 +164,10 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) { return entity; } -Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type) { +Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) { Token token = make_token_ident(name); Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type); + entity->ImplicitValue.id = id; return entity; } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 44fe990f0..d8e1ac908 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -3677,7 +3677,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint default: { gbString str = type_to_string(type); - error(ast_node_token(node), "Invalid or unyet supported compound literal type `%s`", str); + error(ast_node_token(node), "Invalid compound literal type `%s`", str); gb_string_free(str); goto error; } break; @@ -4187,6 +4187,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = write_expr_to_string(str, pt->type); case_end; + case_ast_node(mt, MaybeType, node); + str = gb_string_appendc(str, "?"); + str = write_expr_to_string(str, mt->type); + case_end; + case_ast_node(at, ArrayType, node); str = gb_string_appendc(str, "["); str = write_expr_to_string(str, at->count); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index e7122c072..8f355b5ae 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -1102,8 +1102,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { BasicKind kind = t->Basic.kind; if (kind < gb_count_of(basic_type_sizes)) { i64 size = basic_type_sizes[kind]; - if (size > 0) + if (size > 0) { return size; + } } if (kind == Basic_string) { return 2 * s.word_size; @@ -1114,8 +1115,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { case Type_Array: { i64 count = t->Array.count; - if (count == 0) + if (count == 0) { return 0; + } i64 align = type_align_of(s, allocator, t->Array.elem); i64 size = type_size_of(s, allocator, t->Array.elem); i64 alignment = align_formula(size, align); @@ -1124,8 +1126,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { case Type_Vector: { i64 count = t->Vector.count; - if (count == 0) + if (count == 0) { return 0; + } // i64 align = type_align_of(s, allocator, t->Vector.elem); i64 bit_size = 8*type_size_of(s, allocator, t->Vector.elem); if (is_type_boolean(t->Vector.elem)) { @@ -1169,8 +1172,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { // NOTE(bill): Zeroth field is invalid for (isize i = 1; i < count; i++) { i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); - if (max < size) + if (max < size) { max = size; + } } // NOTE(bill): Align to int i64 align = type_align_of(s, allocator, t); @@ -1184,8 +1188,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { i64 max = 0; for (isize i = 0; i < count; i++) { i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); - if (max < size) + if (max < size) { max = size; + } } // TODO(bill): Is this how it should work? i64 align = type_align_of(s, allocator, t); @@ -1211,7 +1216,6 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) return t->Record.struct_offsets[index]; } } else if (t->kind == Type_Basic) { - gb_printf_err("here!!\n"); if (t->Basic.kind == Basic_string) { switch (index) { case 0: return 0; diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 9a4508aa2..20868d475 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -2306,21 +2306,22 @@ void ssa_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaV } ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) { - ssaValue *value = *map_get(&proc->module->members, hash_string(name)); - return value; + ssaValue **value = map_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); + return *value; } -ssaValue *ssa_emit_implicit_value(ssaProcedure *proc, Entity *e) { - String name = e->token.string; - ssaValue *g = NULL; - if (name == "context") { - g = ssa_emit_load(proc, ssa_find_global_variable(proc, make_string("__context"))); - } - GB_ASSERT(g != NULL); - return g; +ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) { + Entity *e = proc->module->info->implicit_values[id]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + Entity *backing = e->ImplicitValue.backing; + ssaValue **value = map_get(&proc->module->values, hash_pointer(backing)); + GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string)); + return *value; } + ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { switch (expr->kind) { case_ast_node(bl, BasicLit, expr); @@ -2338,7 +2339,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } else if (e->kind == Entity_Nil) { return ssa_make_value_nil(proc->module->allocator, tv->type); } else if (e->kind == Entity_ImplicitValue) { - return ssa_emit_implicit_value(proc, e); + return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id)); } auto *found = map_get(&proc->module->values, hash_pointer(e)); @@ -3069,8 +3070,6 @@ 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) { return ssa_emit_global_string(proc, tv->value.value_string); } else { @@ -3084,8 +3083,7 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { ssaValue *value = NULL; if (tv->mode == Addressing_Variable) { - ssaAddr addr = ssa_build_addr(proc, expr); - value = ssa_lvalue_load(proc, addr); + value = ssa_lvalue_load(proc, ssa_build_addr(proc, expr)); } else { value = ssa_build_single_expr(proc, expr, tv); } @@ -3122,8 +3120,6 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { Entity *e = entity_of_ident(proc->module->info, expr); TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr)); - // GB_ASSERT(tv == NULL || tv->mode == Addressing_Variable); - if (e->kind == Entity_Constant) { if (base_type(e->type) == t_string) { @@ -3148,10 +3144,8 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { } else if (e->kind == Entity_Variable && e->Variable.anonymous) { v = ssa_add_using_variable(proc, e); } else if (e->kind == Entity_ImplicitValue) { - ssaValue *g = ssa_emit_implicit_value(proc, e); - // NOTE(bill): Create a copy as it's by value - v = ssa_add_local_generated(proc, ssa_type(g)); - ssa_emit_store(proc, v, g); + // TODO(bill): Should a copy be made? + v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id); } if (v == NULL) { @@ -4210,7 +4204,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssa_open_scope(proc); defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); - ssaValue *context_ptr = ssa_find_global_variable(proc, make_string("__context")); + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); ssaValue *prev_context = ssa_add_local_generated(proc, t_context); ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); @@ -4229,7 +4223,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssa_open_scope(proc); defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); - ssaValue *context_ptr = ssa_find_global_variable(proc, make_string("__context")); + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); ssaValue *prev_context = ssa_add_local_generated(proc, t_context); ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); diff --git a/src/gb/gb.h b/src/gb/gb.h index df43cc75c..c1a461cbc 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -1928,7 +1928,7 @@ typedef union gbFileDescriptor { typedef struct gbFileOperations gbFileOperations; -#define GB_FILE_OPEN_PROC(name) gbFileError name(gbFileDescriptor *fd, gbFileOperations const **ops, gbFileMode mode, char const *filename) +#define GB_FILE_OPEN_PROC(name) gbFileError name(gbFileDescriptor *fd, gbFileOperations *ops, gbFileMode mode, char const *filename) #define GB_FILE_READ_AT_PROC(name) b32 name(gbFileDescriptor fd, void *buffer, isize size, i64 offset, isize *bytes_read) #define GB_FILE_WRITE_AT_PROC(name) b32 name(gbFileDescriptor fd, void const *buffer, isize size, i64 offset, isize *bytes_written) #define GB_FILE_SEEK_PROC(name) b32 name(gbFileDescriptor fd, i64 offset, gbSeekWhenceType whence, i64 *new_offset) @@ -1958,11 +1958,11 @@ extern gbFileOperations const gbDefaultFileOperations; typedef u64 gbFileTime; typedef struct gbFile { - gbFileOperations const *ops; - gbFileDescriptor fd; - char const * filename; - gbFileTime last_write_time; - // gbDirInfo * dir_info; // TODO(bill): Get directory info + gbFileOperations ops; + gbFileDescriptor fd; + char const * filename; + gbFileTime last_write_time; + // gbDirInfo * dir_info; // TODO(bill): Get directory info } gbFile; // TODO(bill): gbAsyncFile @@ -1980,7 +1980,7 @@ GB_DEF gbFile *const gb_file_get_standard(gbFileStandardType std); GB_DEF gbFileError gb_file_create (gbFile *file, char const *filename); GB_DEF gbFileError gb_file_open (gbFile *file, char const *filename); GB_DEF gbFileError gb_file_open_mode (gbFile *file, gbFileMode mode, char const *filename); -GB_DEF gbFileError gb_file_new (gbFile *file, gbFileDescriptor fd, gbFileOperations const *ops, char const *filename); +GB_DEF gbFileError gb_file_new (gbFile *file, gbFileDescriptor fd, gbFileOperations ops, char const *filename); GB_DEF b32 gb_file_read_at_check (gbFile *file, void *buffer, isize size, i64 offset, isize *bytes_read); GB_DEF b32 gb_file_write_at_check(gbFile *file, void const *buffer, isize size, i64 offset, isize *bytes_written); GB_DEF b32 gb_file_read_at (gbFile *file, void *buffer, isize size, i64 offset); @@ -7299,7 +7299,7 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) { } fd->p = handle; - *ops = &gbDefaultFileOperations; + *ops = gbDefaultFileOperations; return gbFileError_None; } @@ -7381,7 +7381,7 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) { return gbFileError_Invalid; } - *ops = &gbDefaultFileOperations; + *ops = gbDefaultFileOperations; return gbFileError_None; } @@ -7389,7 +7389,7 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) { -gbFileError gb_file_new(gbFile *f, gbFileDescriptor fd, gbFileOperations const *ops, char const *filename) { +gbFileError gb_file_new(gbFile *f, gbFileDescriptor fd, gbFileOperations ops, char const *filename) { gbFileError err = gbFileError_None; f->ops = ops; @@ -7428,20 +7428,20 @@ gbFileError gb_file_close(gbFile *f) { return gbFileError_Invalid; #endif - if (!f->ops) f->ops = &gbDefaultFileOperations; - f->ops->close(f->fd); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + f->ops.close(f->fd); return gbFileError_None; } gb_inline b32 gb_file_read_at_check(gbFile *f, void *buffer, isize size, i64 offset, isize *bytes_read) { - if (!f->ops) f->ops = &gbDefaultFileOperations; - return f->ops->read_at(f->fd, buffer, size, offset, bytes_read); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + return f->ops.read_at(f->fd, buffer, size, offset, bytes_read); } gb_inline b32 gb_file_write_at_check(gbFile *f, void const *buffer, isize size, i64 offset, isize *bytes_written) { - if (!f->ops) f->ops = &gbDefaultFileOperations; - return f->ops->write_at(f->fd, buffer, size, offset, bytes_written); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + return f->ops.write_at(f->fd, buffer, size, offset, bytes_written); } @@ -7455,30 +7455,30 @@ gb_inline b32 gb_file_write_at(gbFile *f, void const *buffer, isize size, i64 of gb_inline i64 gb_file_seek(gbFile *f, i64 offset) { i64 new_offset = 0; - if (!f->ops) f->ops = &gbDefaultFileOperations; - f->ops->seek(f->fd, offset, gbSeekWhence_Begin, &new_offset); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + f->ops.seek(f->fd, offset, gbSeekWhence_Begin, &new_offset); return new_offset; } gb_inline i64 gb_file_seek_to_end(gbFile *f) { i64 new_offset = 0; - if (!f->ops) f->ops = &gbDefaultFileOperations; - f->ops->seek(f->fd, 0, gbSeekWhence_End, &new_offset); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + f->ops.seek(f->fd, 0, gbSeekWhence_End, &new_offset); return new_offset; } // NOTE(bill): Skips a certain amount of bytes gb_inline i64 gb_file_skip(gbFile *f, i64 bytes) { i64 new_offset = 0; - if (!f->ops) f->ops = &gbDefaultFileOperations; - f->ops->seek(f->fd, bytes, gbSeekWhence_Current, &new_offset); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + f->ops.seek(f->fd, bytes, gbSeekWhence_Current, &new_offset); return new_offset; } gb_inline i64 gb_file_tell(gbFile *f) { i64 new_offset = 0; - if (!f->ops) f->ops = &gbDefaultFileOperations; - f->ops->seek(f->fd, 0, gbSeekWhence_Current, &new_offset); + if (!f->ops.read_at) f->ops = gbDefaultFileOperations; + f->ops.seek(f->fd, 0, gbSeekWhence_Current, &new_offset); return new_offset; } gb_inline b32 gb_file_read (gbFile *f, void *buffer, isize size) { return gb_file_read_at(f, buffer, size, gb_file_tell(f)); } @@ -7516,7 +7516,7 @@ gb_global gbFile gb__std_files[gbFileStandard_Count] = {{0}}; gb_inline gbFile *const gb_file_get_standard(gbFileStandardType std) { if (!gb__std_file_set) { - #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.p = v; gb__std_files[type].ops = &gbDefaultFileOperations + #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.p = v; gb__std_files[type].ops = gbDefaultFileOperations GB__SET_STD_FILE(gbFileStandard_Input, GetStdHandle(STD_INPUT_HANDLE)); GB__SET_STD_FILE(gbFileStandard_Output, GetStdHandle(STD_OUTPUT_HANDLE)); GB__SET_STD_FILE(gbFileStandard_Error, GetStdHandle(STD_ERROR_HANDLE)); @@ -7555,7 +7555,7 @@ b32 gb_file_exists(char const *name) { gb_inline gbFile *const gb_file_get_standard(gbFileStandardType std) { if (!gb__std_file_set) { - #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.i = v; gb__std_files[type].ops = &gbDefaultFileOperations + #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.i = v; gb__std_files[type].ops = gbDefaultFileOperations GB__SET_STD_FILE(gbFileStandard_Input, 0); GB__SET_STD_FILE(gbFileStandard_Output, 1); GB__SET_STD_FILE(gbFileStandard_Error, 2); diff --git a/src/string.cpp b/src/string.cpp index e15498cd1..a8e8b5583 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -115,17 +115,17 @@ GB_COMPARE_PROC(string_cmp_proc) { } -bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; } -bool operator !=(String a, String b) { return !operator==(a, b); } -bool operator < (String a, String b) { return string_compare(a, b) < 0; } -bool operator > (String a, String b) { return string_compare(a, b) > 0; } -bool operator <=(String a, String b) { return string_compare(a, b) <= 0; } -bool operator >=(String a, String b) { return string_compare(a, b) >= 0; } +gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; } +gb_inline bool operator !=(String a, String b) { return !operator==(a, b); } +gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; } +gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; } +gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; } +gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; } -template bool operator ==(String a, char const (&b)[N]) { return a == make_string(b); } -template bool operator !=(String a, char const (&b)[N]) { return a != make_string(b); } -template bool operator ==(char const (&a)[N], String b) { return make_string(a) == b; } -template bool operator !=(char const (&a)[N], String b) { return make_string(a) != b; } +template gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); } +template gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); } +template gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; } +template gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; }