From 7792f009b8d8fbc4949b8110ebbcffc943878cae Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Wed, 23 Nov 2016 14:41:20 +0000 Subject: [PATCH] Numpty forgot to add .c files --- README.md | 2 + logo-slim.png | Bin 0 -> 251710 bytes src/array.c | 235 ++ src/checker/checker.c | 1353 ++++++++++ src/checker/decl.c | 545 +++++ src/checker/entity.c | 179 ++ src/checker/expr.c | 4465 +++++++++++++++++++++++++++++++++ src/checker/stmt.c | 1130 +++++++++ src/checker/types.c | 1487 +++++++++++ src/common.c | 195 ++ src/exact_value.c | 400 +++ src/main.c | 272 +++ src/old_vm.c | 1305 ++++++++++ src/parser.c | 3250 ++++++++++++++++++++++++ src/printer.c | 221 ++ src/ssa.c | 5419 +++++++++++++++++++++++++++++++++++++++++ src/ssa_opt.c | 493 ++++ src/ssa_print.c | 1439 +++++++++++ src/string.c | 422 ++++ src/timings.c | 105 + src/tokenizer.c | 816 +++++++ src/unicode.c | 66 + 22 files changed, 23799 insertions(+) create mode 100644 logo-slim.png create mode 100644 src/array.c create mode 100644 src/checker/checker.c create mode 100644 src/checker/decl.c create mode 100644 src/checker/entity.c create mode 100644 src/checker/expr.c create mode 100644 src/checker/stmt.c create mode 100644 src/checker/types.c create mode 100644 src/common.c create mode 100644 src/exact_value.c create mode 100644 src/main.c create mode 100644 src/old_vm.c create mode 100644 src/parser.c create mode 100644 src/printer.c create mode 100644 src/ssa.c create mode 100644 src/ssa_opt.c create mode 100644 src/ssa_print.c create mode 100644 src/string.c create mode 100644 src/timings.c create mode 100644 src/tokenizer.c create mode 100644 src/unicode.c diff --git a/README.md b/README.md index 0a5496ba6..5b962f919 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Odin logo + # The Odin Programming Language Odin is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals: diff --git a/logo-slim.png b/logo-slim.png new file mode 100644 index 0000000000000000000000000000000000000000..2b70e6a0c8a01ef6fffff2a59d1d7409551a529a GIT binary patch literal 251710 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 q5GNuR2=Dc{sRLv~h%*1f2jo=<-tZq45@ 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�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 0); + +#define Array(Type_) struct { \ + gbAllocator allocator; \ + Type_ * e; \ + isize count; \ + isize capacity; \ +} + +typedef Array(void) ArrayVoid; + +#define array_init_reserve(x_, allocator_, init_capacity_) do { \ + GB_ASSERT((x_) != NULL); \ + void **e = cast(void **)&((x_)->e); \ + (x_)->allocator = (allocator_); \ + (x_)->count = 0; \ + (x_)->capacity = (init_capacity_); \ + *e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \ +} while (0) + +#define array_init_count(x_, allocator_, init_count_) do { \ + GB_ASSERT((x_) != NULL); \ + void **e = cast(void **)&((x_)->e); \ + (x_)->allocator = (allocator_); \ + (x_)->count = (init_count_); \ + (x_)->capacity = (init_count_); \ + *e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \ +} while (0) + +#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0) +#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0) +#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0) + +#define array_grow(x_, min_capacity_) do { \ + isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \ + if (new_capacity < (min_capacity_)) { \ + new_capacity = (min_capacity_); \ + } \ + array_set_capacity(x_, new_capacity); \ +} while (0) + +#define array_add(x_, item_) do { \ + if ((x_)->capacity < (x_)->count+1) { \ + array_grow(x_, 0); \ + } \ + (x_)->e[(x_)->count++] = item_; \ +} while (0) + +#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0) +#define array_clear(x_) do { (x_)->count = 0; } while (0) + +#define array_resize(x_, new_count_) do { \ + if ((x_)->capacity < (new_count_)) { \ + array_grow((x_), (new_count_)); \ + } \ + (x_)->count = (new_count_); \ +} while (0) + +#define array_reserve(x_, new_capacity_) do { \ + if ((x_)->capacity < (new_capacity_)) { \ + array_set_capacity((x_), (new_capacity_)); \ + } \ +} while (0) + + + + +void array__set_capacity(void *ptr, isize capacity, isize element_size) { + GB_ASSERT(ptr != NULL); + ArrayVoid *x = cast(ArrayVoid *)ptr; + + GB_ASSERT(element_size > 0); + + if (capacity == x->capacity) { + return; + } + + if (capacity < x->count) { + if (x->capacity < capacity) { + isize new_capacity = ARRAY_GROW_FORMULA(x->capacity); + if (new_capacity < capacity) { + new_capacity = capacity; + } + array__set_capacity(ptr, new_capacity, element_size); + } + x->count = capacity; + } + + { + // TODO(bill): Resize rather than copy and delete + void *new_data = gb_alloc(x->allocator, element_size*capacity); + gb_memmove(new_data, x->e, element_size*x->count); + gb_free(x->allocator, x->e); + x->capacity = capacity; + x->e = new_data; + } +} + + +#if 0 +template +struct Array { + gbAllocator allocator; + T * data; + isize count; + isize capacity; + + T &operator[](isize index) { + GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds"); + return data[index]; + } + + T const &operator[](isize index) const { + GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds"); + return data[index]; + } +}; + +template void array_init (Array *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0)); +template void array_init_count (Array *array, gbAllocator a, isize count); +template Array array_make (T *data, isize count, isize capacity); +template void array_free (Array *array); +template void array_add (Array *array, T const &t); +template T array_pop (Array *array); +template void array_clear (Array *array); +template void array_reserve (Array *array, isize capacity); +template void array_resize (Array *array, isize count); +template void array_set_capacity(Array *array, isize capacity); + + +template +void array_init(Array *array, gbAllocator a, isize init_capacity) { + array->allocator = a; + array->data = gb_alloc_array(a, T, init_capacity); + array->count = 0; + array->capacity = init_capacity; +} + +template +void array_init_count(Array *array, gbAllocator a, isize count) { + array->allocator = a; + array->data = gb_alloc_array(a, T, count); + array->count = count; + array->capacity = count; +} + + +template +Array array_make(T *data, isize count, isize capacity) { + Array a = {0}; + a.data = data; + a.count = count; + a.capacity = capacity; + return a; +} + + +template +void array_free(Array *array) { + if (array->allocator.proc != NULL) { + gb_free(array->allocator, array->data); + } + array->count = 0; + array->capacity = 0; +} + +template +void array__grow(Array *array, isize min_capacity) { + isize new_capacity = ARRAY_GROW_FORMULA(array->capacity); + if (new_capacity < min_capacity) { + new_capacity = min_capacity; + } + array_set_capacity(array, new_capacity); +} + +template +void array_add(Array *array, T const &t) { + if (array->capacity < array->count+1) { + array__grow(array, 0); + } + array->data[array->count] = t; + array->count++; +} + +template +T array_pop(Array *array) { + GB_ASSERT(array->count > 0); + array->count--; + return array->data[array->count]; +} + +template +void array_clear(Array *array) { + array->count = 0; +} + +template +void array_reserve(Array *array, isize capacity) { + if (array->capacity < capacity) { + array_set_capacity(array, capacity); + } +} + +template +void array_resize(Array *array, isize count) { + if (array->capacity < count) { + array__grow(array, count); + } + array->count = count; +} + +template +void array_set_capacity(Array *array, isize capacity) { + if (capacity == array->capacity) { + return; + } + + if (capacity < array->count) { + array_resize(array, capacity); + } + + T *new_data = NULL; + if (capacity > 0) { + new_data = gb_alloc_array(array->allocator, T, capacity); + gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity); + } + gb_free(array->allocator, array->data); + array->data = new_data; + array->capacity = capacity; +} + + + +#endif diff --git a/src/checker/checker.c b/src/checker/checker.c new file mode 100644 index 000000000..889efa1d3 --- /dev/null +++ b/src/checker/checker.c @@ -0,0 +1,1353 @@ +#include "../exact_value.c" +#include "entity.c" +#include "types.c" + +#define MAP_TYPE Entity * +#define MAP_PROC map_entity_ +#define MAP_NAME MapEntity +#include "../map.c" + +typedef enum AddressingMode { + Addressing_Invalid, + Addressing_NoValue, + Addressing_Value, + Addressing_Variable, + Addressing_Constant, + Addressing_Type, + Addressing_Builtin, + Addressing_Count, +} AddressingMode; + +typedef struct Operand { + AddressingMode mode; + Type * type; + ExactValue value; + AstNode * expr; + BuiltinProcId builtin_id; +} Operand; + +typedef struct TypeAndValue { + AddressingMode mode; + Type * type; + ExactValue value; +} TypeAndValue; + + + +typedef struct DeclInfo { + Scope *scope; + + Entity **entities; + isize entity_count; + + AstNode *type_expr; + AstNode *init_expr; + AstNode *proc_decl; // AstNode_ProcDecl + u32 var_decl_tags; + + MapBool deps; // Key: Entity * +} DeclInfo; + +typedef struct ExprInfo { + bool is_lhs; // Debug info + AddressingMode mode; + Type * type; // Type_Basic + ExactValue value; +} ExprInfo; + +ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) { + ExprInfo ei = {is_lhs, mode, type, value}; + return ei; +} + +typedef struct ProcedureInfo { + AstFile * file; + Token token; + DeclInfo *decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStatement + u32 tags; +} ProcedureInfo; + +typedef struct Scope { + Scope * parent; + Scope * prev, *next; + Scope * first_child; + Scope * last_child; + MapEntity elements; // Key: String + MapEntity implicit; // Key: String + + Array(Scope *) shared; + Array(Scope *) imported; + bool is_proc; + bool is_global; + bool is_file; + bool is_init; + AstFile * file; +} Scope; +gb_global Scope *universal_scope = NULL; + +typedef enum ExprKind { + Expr_Expr, + Expr_Stmt, +} ExprKind; + +typedef enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_new, + BuiltinProc_new_slice, + + BuiltinProc_size_of, + BuiltinProc_size_of_val, + BuiltinProc_align_of, + BuiltinProc_align_of_val, + BuiltinProc_offset_of, + BuiltinProc_offset_of_val, + BuiltinProc_type_of_val, + + BuiltinProc_type_info, + BuiltinProc_type_info_of_val, + + BuiltinProc_compile_assert, + BuiltinProc_assert, + BuiltinProc_panic, + + BuiltinProc_copy, + BuiltinProc_append, + + BuiltinProc_swizzle, + + // BuiltinProc_ptr_offset, + // BuiltinProc_ptr_sub, + BuiltinProc_slice_ptr, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + + BuiltinProc_enum_to_string, + + BuiltinProc_Count, +} BuiltinProcId; +typedef struct BuiltinProc { + String name; + isize arg_count; + bool variadic; + ExprKind kind; +} BuiltinProc; +gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { + {STR_LIT(""), 0, false, Expr_Stmt}, + + {STR_LIT("new"), 1, false, Expr_Expr}, + {STR_LIT("new_slice"), 2, true, Expr_Expr}, + + {STR_LIT("size_of"), 1, false, Expr_Expr}, + {STR_LIT("size_of_val"), 1, false, Expr_Expr}, + {STR_LIT("align_of"), 1, false, Expr_Expr}, + {STR_LIT("align_of_val"), 1, false, Expr_Expr}, + {STR_LIT("offset_of"), 2, false, Expr_Expr}, + {STR_LIT("offset_of_val"), 1, false, Expr_Expr}, + {STR_LIT("type_of_val"), 1, false, Expr_Expr}, + + {STR_LIT("type_info"), 1, false, Expr_Expr}, + {STR_LIT("type_info_of_val"), 1, false, Expr_Expr}, + + {STR_LIT("compile_assert"), 1, false, Expr_Stmt}, + {STR_LIT("assert"), 1, false, Expr_Stmt}, + {STR_LIT("panic"), 1, false, Expr_Stmt}, + + {STR_LIT("copy"), 2, false, Expr_Expr}, + {STR_LIT("append"), 2, false, Expr_Expr}, + + {STR_LIT("swizzle"), 1, true, Expr_Expr}, + + // {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, + // {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, + {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, + + {STR_LIT("min"), 2, false, Expr_Expr}, + {STR_LIT("max"), 2, false, Expr_Expr}, + {STR_LIT("abs"), 1, false, Expr_Expr}, + + {STR_LIT("enum_to_string"), 1, false, Expr_Expr}, +}; + +typedef enum ImplicitValueId { + ImplicitValue_Invalid, + + ImplicitValue_context, + + ImplicitValue_Count, +} ImplicitValueId; +typedef struct ImplicitValueInfo { + String name; + String backing_name; + Type * type; +} ImplicitValueInfo; +// NOTE(bill): This is initialized later +gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {0}; + + + +typedef struct CheckerContext { + Scope * scope; + DeclInfo *decl; + u32 stmt_state_flags; +} CheckerContext; + +#define MAP_TYPE TypeAndValue +#define MAP_PROC map_tav_ +#define MAP_NAME MapTypeAndValue +#include "../map.c" + +#define MAP_TYPE Scope * +#define MAP_PROC map_scope_ +#define MAP_NAME MapScope +#include "../map.c" + +#define MAP_TYPE DeclInfo * +#define MAP_PROC map_decl_info_ +#define MAP_NAME MapDeclInfo +#include "../map.c" + +#define MAP_TYPE AstFile * +#define MAP_PROC map_ast_file_ +#define MAP_NAME MapAstFile +#include "../map.c" + +#define MAP_TYPE ExprInfo +#define MAP_PROC map_expr_info_ +#define MAP_NAME MapExprInfo +#include "../map.c" + + +// NOTE(bill): Symbol tables +typedef struct CheckerInfo { + MapTypeAndValue types; // Key: AstNode * | Expression -> Type (and value) + MapEntity definitions; // Key: AstNode * | Identifier -> Entity + MapEntity uses; // Key: AstNode * | Identifier -> Entity + MapScope scopes; // Key: AstNode * | Node -> Scope + MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo + MapDeclInfo entities; // Key: Entity * + MapEntity foreign_procs; // Key: String + MapAstFile files; // Key: String (full path) + MapIsize type_info_map; // Key: Type * + isize type_info_count; + Entity * implicit_values[ImplicitValue_Count]; +} CheckerInfo; + +typedef struct Checker { + Parser * parser; + CheckerInfo info; + + AstFile * curr_ast_file; + BaseTypeSizes sizes; + Scope * global_scope; + Array(ProcedureInfo) procs; // NOTE(bill): Procedures to check + + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; + + CheckerContext context; + + Array(Type *) proc_stack; + bool in_defer; // TODO(bill): Actually handle correctly +} Checker; + +typedef struct CycleChecker { + Array(Entity *) path; // Entity_TypeName +} CycleChecker; + + + + +CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) { + if (cc == NULL) { + return NULL; + } + if (cc->path.e == NULL) { + array_init(&cc->path, heap_allocator()); + } + GB_ASSERT(e != NULL && e->kind == Entity_TypeName); + array_add(&cc->path, e); + return cc; +} + +void cycle_checker_destroy(CycleChecker *cc) { + if (cc != NULL && cc->path.e != NULL) { + array_free(&cc->path); + } +} + + +void init_declaration_info(DeclInfo *d, Scope *scope) { + d->scope = scope; + map_bool_init(&d->deps, heap_allocator()); +} + +DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) { + DeclInfo *d = gb_alloc_item(a, DeclInfo); + init_declaration_info(d, scope); + return d; +} + +void destroy_declaration_info(DeclInfo *d) { + map_bool_destroy(&d->deps); +} + +bool decl_info_has_init(DeclInfo *d) { + if (d->init_expr != NULL) { + return true; + } + if (d->proc_decl != NULL) { + ast_node(pd, ProcDecl, d->proc_decl); + if (pd->body != NULL) { + return true; + } + } + + return false; +} + + + + + +Scope *make_scope(Scope *parent, gbAllocator allocator) { + Scope *s = gb_alloc_item(allocator, Scope); + s->parent = parent; + map_entity_init(&s->elements, heap_allocator()); + map_entity_init(&s->implicit, heap_allocator()); + array_init(&s->shared, heap_allocator()); + array_init(&s->imported, heap_allocator()); + + if (parent != NULL && parent != universal_scope) { + DLIST_APPEND(parent->first_child, parent->last_child, s); + } + return s; +} + +void destroy_scope(Scope *scope) { + for_array(i, scope->elements.entries) { + Entity *e =scope->elements.entries.e[i].value; + if (e->kind == Entity_Variable) { + if (!(e->flags & EntityFlag_Used)) { +#if 0 + warning(e->token, "Unused variable `%.*s`", LIT(e->token.string)); +#endif + } + } + } + + for (Scope *child = scope->first_child; child != NULL; child = child->next) { + destroy_scope(child); + } + + map_entity_destroy(&scope->elements); + map_entity_destroy(&scope->implicit); + array_free(&scope->shared); + array_free(&scope->imported); + + // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) +} + +void add_scope(Checker *c, AstNode *node, Scope *scope) { + GB_ASSERT(node != NULL); + GB_ASSERT(scope != NULL); + map_scope_set(&c->info.scopes, hash_pointer(node), scope); +} + + +void check_open_scope(Checker *c, AstNode *node) { + GB_ASSERT(node != NULL); + GB_ASSERT(node->kind == AstNode_Invalid || + is_ast_node_stmt(node) || + is_ast_node_type(node)); + Scope *scope = make_scope(c->context.scope, c->allocator); + add_scope(c, node, scope); + if (node->kind == AstNode_ProcType) { + scope->is_proc = true; + } + c->context.scope = scope; + c->context.stmt_state_flags |= StmtStateFlag_bounds_check; +} + +void check_close_scope(Checker *c) { + c->context.scope = c->context.scope->parent; +} + +void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) { + bool gone_thru_proc = false; + HashKey key = hash_string(name); + for (Scope *s = scope; s != NULL; s = s->parent) { + Entity **found = map_entity_get(&s->elements, key); + if (found) { + Entity *e = *found; + if (gone_thru_proc) { + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { + continue; + } + } + + if (entity_) *entity_ = e; + if (scope_) *scope_ = s; + return; + } + + if (s->is_proc) { + gone_thru_proc = true; + } else { + // Check shared scopes - i.e. other files @ global scope + for_array(i, s->shared) { + Scope *shared = s->shared.e[i]; + Entity **found = map_entity_get(&shared->elements, key); + if (found) { + Entity *e = *found; + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { + continue; + } + + if (e->scope != shared) { + // Do not return imported entities even #load ones + continue; + } + + if (entity_) *entity_ = e; + if (scope_) *scope_ = shared; + return; + } + } + } + } + + + if (entity_) *entity_ = NULL; + if (scope_) *scope_ = NULL; +} + +Entity *scope_lookup_entity(Scope *s, String name) { + Entity *entity = NULL; + scope_lookup_parent_entity(s, name, NULL, &entity); + return entity; +} + +Entity *current_scope_lookup_entity(Scope *s, String name) { + HashKey key = hash_string(name); + Entity **found = map_entity_get(&s->elements, key); + if (found) { + return *found; + } + for_array(i, s->shared) { + Entity **found = map_entity_get(&s->shared.e[i]->elements, key); + if (found) { + return *found; + } + } + return NULL; +} + + + +Entity *scope_insert_entity(Scope *s, Entity *entity) { + String name = entity->token.string; + HashKey key = hash_string(name); + Entity **found = map_entity_get(&s->elements, key); + if (found) { + return *found; + } + map_entity_set(&s->elements, key, entity); + if (entity->scope == NULL) { + entity->scope = s; + } + return NULL; +} + +void check_scope_usage(Checker *c, Scope *scope) { + // TODO(bill): Use this? +} + + +void add_dependency(DeclInfo *d, Entity *e) { + map_bool_set(&d->deps, hash_pointer(e), cast(bool)true); +} + +void add_declaration_dependency(Checker *c, Entity *e) { + if (e == NULL) { + return; + } + if (c->context.decl != NULL) { + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + if (found) { + add_dependency(c->context.decl, e); + } + } +} + + +void add_global_entity(Entity *entity) { + String name = entity->token.string; + if (gb_memchr(name.text, ' ', name.len)) { + return; // NOTE(bill): `untyped thing` + } + if (scope_insert_entity(universal_scope, entity)) { + compiler_error("double declaration"); + } +} + +void add_global_constant(gbAllocator a, String name, Type *type, ExactValue value) { + Entity *entity = alloc_entity(a, Entity_Constant, NULL, make_token_ident(name), type); + entity->Constant.value = value; + add_global_entity(entity); +} + + + +void init_universal_scope(void) { + // NOTE(bill): No need to free these + gbAllocator a = heap_allocator(); + universal_scope = make_scope(NULL, a); + +// Types + for (isize i = 0; i < gb_count_of(basic_types); i++) { + add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_types[i].Basic.name), &basic_types[i])); + } + for (isize i = 0; i < gb_count_of(basic_type_aliases); i++) { + add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_type_aliases[i].Basic.name), &basic_type_aliases[i])); + } + +// Constants + add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true)); + add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false)); + + add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil)); + +// Builtin Procedures + for (isize i = 0; i < gb_count_of(builtin_procs); i++) { + BuiltinProcId id = cast(BuiltinProcId)i; + Entity *entity = alloc_entity(a, Entity_Builtin, NULL, make_token_ident(builtin_procs[i].name), t_invalid); + entity->Builtin.id = id; + add_global_entity(entity); + } + + t_u8_ptr = make_type_pointer(a, t_u8); + t_int_ptr = make_type_pointer(a, t_int); +} + + + + +void init_checker_info(CheckerInfo *i) { + gbAllocator a = heap_allocator(); + map_tav_init(&i->types, a); + map_entity_init(&i->definitions, a); + map_entity_init(&i->uses, a); + map_scope_init(&i->scopes, a); + map_decl_info_init(&i->entities, a); + map_expr_info_init(&i->untyped, a); + map_entity_init(&i->foreign_procs, a); + map_isize_init(&i->type_info_map, a); + map_ast_file_init(&i->files, a); + i->type_info_count = 0; + +} + +void destroy_checker_info(CheckerInfo *i) { + map_tav_destroy(&i->types); + map_entity_destroy(&i->definitions); + map_entity_destroy(&i->uses); + map_scope_destroy(&i->scopes); + map_decl_info_destroy(&i->entities); + map_expr_info_destroy(&i->untyped); + map_entity_destroy(&i->foreign_procs); + map_isize_destroy(&i->type_info_map); + map_ast_file_destroy(&i->files); +} + + +void init_checker(Checker *c, Parser *parser, BaseTypeSizes sizes) { + gbAllocator a = heap_allocator(); + + c->parser = parser; + init_checker_info(&c->info); + c->sizes = sizes; + + array_init(&c->proc_stack, a); + array_init(&c->procs, a); + + // NOTE(bill): Is this big enough or too small? + isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope)); + isize total_token_count = 0; + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + total_token_count += f->tokens.count; + } + isize arena_size = 2 * item_size * total_token_count; + gb_arena_init_from_allocator(&c->arena, a, arena_size); + gb_arena_init_from_allocator(&c->tmp_arena, a, arena_size); + + + c->allocator = gb_arena_allocator(&c->arena); + c->tmp_allocator = gb_arena_allocator(&c->tmp_arena); + + c->global_scope = make_scope(universal_scope, c->allocator); + c->context.scope = c->global_scope; +} + +void destroy_checker(Checker *c) { + destroy_checker_info(&c->info); + destroy_scope(c->global_scope); + array_free(&c->proc_stack); + array_free(&c->procs); + + gb_arena_free(&c->arena); +} + + +TypeAndValue *type_and_value_of_expression(CheckerInfo *i, AstNode *expression) { + TypeAndValue *found = map_tav_get(&i->types, hash_pointer(expression)); + return found; +} + + +Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) { + if (identifier->kind == AstNode_Ident) { + Entity **found = map_entity_get(&i->definitions, hash_pointer(identifier)); + if (found) { + return *found; + } + found = map_entity_get(&i->uses, hash_pointer(identifier)); + if (found) { + return *found; + } + } + return NULL; +} + +Type *type_of_expr(CheckerInfo *i, AstNode *expression) { + TypeAndValue *found = type_and_value_of_expression(i, expression); + if (found) { + return found->type; + } + if (expression->kind == AstNode_Ident) { + Entity *entity = entity_of_ident(i, expression); + if (entity) { + return entity->type; + } + } + + return NULL; +} + + +void add_untyped(CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value) { + map_expr_info_set(&i->untyped, hash_pointer(expression), make_expr_info(lhs, mode, basic_type, value)); +} + +void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) { + GB_ASSERT(expression != NULL); + if (mode == Addressing_Invalid) { + return; + } + + if (mode == Addressing_Constant) { + if (is_type_constant_type(type)) { + GB_ASSERT(value.kind != ExactValue_Invalid); + if (!(type != t_invalid || is_type_constant_type(type))) { + compiler_error("add_type_and_value - invalid type: %s", type_to_string(type)); + } + } + } + + TypeAndValue tv = {0}; + tv.type = type; + tv.value = value; + tv.mode = mode; + map_tav_set(&i->types, hash_pointer(expression), tv); +} + +void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) { + GB_ASSERT(identifier != NULL); + if (identifier->kind == AstNode_Ident) { + GB_ASSERT(identifier->kind == AstNode_Ident); + HashKey key = hash_pointer(identifier); + map_entity_set(&i->definitions, key, entity); + } else { + // NOTE(bill): Error should handled elsewhere + } +} + +bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { + if (str_ne(entity->token.string, str_lit("_"))) { + Entity *insert_entity = scope_insert_entity(scope, entity); + if (insert_entity) { + Entity *up = insert_entity->using_parent; + if (up != NULL) { + error(entity->token, + "Redeclararation of `%.*s` in this scope through `using`\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + return false; + } else { + TokenPos pos = insert_entity->token.pos; + if (token_pos_are_equal(pos, entity->token.pos)) { + // NOTE(bill): Error should have been handled already + return false; + } + error(entity->token, + "Redeclararation of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(pos.file), pos.line, pos.column); + return false; + } + } + } + if (identifier != NULL) { + add_entity_definition(&c->info, identifier, entity); + } + return true; +} + +void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) { + GB_ASSERT(identifier != NULL); + if (identifier->kind != AstNode_Ident) { + return; + } + map_entity_set(&c->info.uses, hash_pointer(identifier), entity); + add_declaration_dependency(c, entity); // TODO(bill): Should this be here? +} + + +void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { + GB_ASSERT(str_eq(identifier->Ident.string, e->token.string)); + add_entity(c, e->scope, identifier, e); + map_decl_info_set(&c->info.entities, hash_pointer(e), d); +} + +void add_type_info_type(Checker *c, Type *t) { + if (t == NULL) { + return; + } + t = default_type(t); + if (is_type_untyped(t)) { + return; // Could be nil + } + + if (map_isize_get(&c->info.type_info_map, hash_pointer(t)) != NULL) { + // Types have already been added + return; + } + + isize ti_index = -1; + for_array(i, c->info.type_info_map.entries) { + MapIsizeEntry *e = &c->info.type_info_map.entries.e[i]; + Type *prev_type = cast(Type *)e->key.ptr; + if (are_types_identical(t, prev_type)) { + // Duplicate entry + ti_index = e->value; + break; + } + } + if (ti_index < 0) { + // Unique entry + // NOTE(bill): map entries grow linearly and in order + ti_index = c->info.type_info_count; + c->info.type_info_count++; + } + map_isize_set(&c->info.type_info_map, hash_pointer(t), ti_index); + + + + + // Add nested types + + if (t->kind == Type_Named) { + // NOTE(bill): Just in case + add_type_info_type(c, t->Named.base); + return; + } + + Type *bt = base_type(t); + add_type_info_type(c, bt); + + switch (bt->kind) { + case Type_Basic: { + switch (bt->Basic.kind) { + case Basic_string: + add_type_info_type(c, t_u8_ptr); + add_type_info_type(c, t_int); + break; + case Basic_any: + add_type_info_type(c, t_type_info_ptr); + add_type_info_type(c, t_rawptr); + break; + } + } break; + + case Type_Maybe: + add_type_info_type(c, bt->Maybe.elem); + add_type_info_type(c, t_bool); + break; + + case Type_Pointer: + add_type_info_type(c, bt->Pointer.elem); + break; + + case Type_Array: + add_type_info_type(c, bt->Array.elem); + add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem)); + add_type_info_type(c, t_int); + break; + case Type_Slice: + add_type_info_type(c, bt->Slice.elem); + add_type_info_type(c, make_type_pointer(c->allocator, bt->Slice.elem)); + add_type_info_type(c, t_int); + break; + case Type_Vector: + add_type_info_type(c, bt->Vector.elem); + add_type_info_type(c, t_int); + break; + + case Type_Record: { + switch (bt->Record.kind) { + case TypeRecord_Enum: + add_type_info_type(c, bt->Record.enum_base); + break; + + case TypeRecord_Union: + add_type_info_type(c, t_int); + /* fallthrough */ + default: + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->Record.fields[i]; + add_type_info_type(c, f->type); + } + break; + } + } break; + + case Type_Tuple: + for (isize i = 0; i < bt->Tuple.variable_count; i++) { + Entity *var = bt->Tuple.variables[i]; + add_type_info_type(c, var->type); + } + break; + + case Type_Proc: + add_type_info_type(c, bt->Proc.params); + add_type_info_type(c, bt->Proc.results); + break; + } +} + + +void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) { + ProcedureInfo info = {0}; + info.file = file; + info.token = token; + info.decl = decl; + info.type = type; + info.body = body; + info.tags = tags; + array_add(&c->procs, info); +} + +void push_procedure(Checker *c, Type *type) { + array_add(&c->proc_stack, type); +} + +void pop_procedure(Checker *c) { + array_pop(&c->proc_stack); +} + +Type *const curr_procedure(Checker *c) { + isize count = c->proc_stack.count; + if (count > 0) { + return c->proc_stack.e[count-1]; + } + return NULL; +} + +void add_curr_ast_file(Checker *c, AstFile *file) { + TokenPos zero_pos = {0}; + global_error_collector.prev = zero_pos; + c->curr_ast_file = file; + c->context.decl = file->decl_info; +} + + + + +void add_dependency_to_map(MapEntity *map, CheckerInfo *info, Entity *node) { + if (node == NULL) { + return; + } + if (map_entity_get(map, hash_pointer(node)) != NULL) { + return; + } + map_entity_set(map, hash_pointer(node), node); + + + DeclInfo **found = map_decl_info_get(&info->entities, hash_pointer(node)); + if (found == NULL) { + return; + } + + DeclInfo *decl = *found; + for_array(i, decl->deps.entries) { + Entity *e = cast(Entity *)decl->deps.entries.e[i].key.ptr; + add_dependency_to_map(map, info, e); + } +} + +MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) { + MapEntity map = {0}; // Key: Entity * + map_entity_init(&map, heap_allocator()); + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->scope->is_global) { + // NOTE(bill): Require runtime stuff + add_dependency_to_map(&map, info, e); + } + } + + add_dependency_to_map(&map, info, start); + + return map; +} + + + + +#include "expr.c" +#include "decl.c" +#include "stmt.c" + +void init_preload_types(Checker *c) { + if (t_type_info == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Type_Info")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Type_Info`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_type_info = e->type; + t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); + GB_ASSERT(is_type_union(e->type)); + TypeRecord *record = &base_type(e->type)->Record; + + t_type_info_member = record->other_fields[0]->type; + t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member); + + if (record->field_count != 18) { + compiler_error("Invalid `Type_Info` layout"); + } + t_type_info_named = record->fields[ 1]->type; + t_type_info_integer = record->fields[ 2]->type; + t_type_info_float = record->fields[ 3]->type; + t_type_info_any = record->fields[ 4]->type; + t_type_info_string = record->fields[ 5]->type; + t_type_info_boolean = record->fields[ 6]->type; + t_type_info_pointer = record->fields[ 7]->type; + t_type_info_maybe = record->fields[ 8]->type; + t_type_info_procedure = record->fields[ 9]->type; + t_type_info_array = record->fields[10]->type; + t_type_info_slice = record->fields[11]->type; + t_type_info_vector = record->fields[12]->type; + t_type_info_tuple = record->fields[13]->type; + t_type_info_struct = record->fields[14]->type; + t_type_info_union = record->fields[15]->type; + t_type_info_raw_union = record->fields[16]->type; + t_type_info_enum = record->fields[17]->type; + } + + if (t_allocator == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Allocator")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Allocator`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_allocator = e->type; + t_allocator_ptr = make_type_pointer(c->allocator, t_allocator); + } + + if (t_context == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Context")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Context`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_context = e->type; + t_context_ptr = make_type_pointer(c->allocator, 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_global_entity(Checker *c, EntityKind kind) { + for_array(i, c->info.entities.entries) { + MapDeclInfoEntry *entry = &c->info.entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->kind == kind) { + DeclInfo *d = entry->value; + + add_curr_ast_file(c, d->scope->file); + + if (d->scope == e->scope) { + if (kind != Entity_Procedure && str_eq(e->token.string, str_lit("main"))) { + if (e->scope->is_init) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + } else if (e->scope->is_global && str_eq(e->token.string, str_lit("main"))) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + + Scope *prev_scope = c->context.scope; + c->context.scope = d->scope; + check_entity_decl(c, e, d, NULL, NULL); + } + } + } +} + +void check_parsed_files(Checker *c) { + AstNodeArray import_decls; + array_init(&import_decls, heap_allocator()); + + MapScope file_scopes; // Key: String (fullpath) + map_scope_init(&file_scopes, heap_allocator()); + + // Map full filepaths to Scopes + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + Scope *scope = NULL; + scope = make_scope(c->global_scope, c->allocator); + scope->is_global = f->is_global_scope; + scope->is_file = true; + scope->file = f; + if (i == 0) { + // NOTE(bill): First file is always the initial file + // thus it must contain main + scope->is_init = true; + } + + if (scope->is_global) { + array_add(&c->global_scope->shared, scope); + } + + f->scope = scope; + f->decl_info = make_declaration_info(c->allocator, f->scope); + HashKey key = hash_string(f->tokenizer.fullpath); + map_scope_set(&file_scopes, key, scope); + map_ast_file_set(&c->info.files, key, f); + } + + // Collect Entities + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + + for_array(decl_index, f->decls) { + AstNode *decl = f->decls.e[decl_index]; + if (!is_ast_node_decl(decl)) { + continue; + } + + switch (decl->kind) { + case_ast_node(bd, BadDecl, decl); + case_end; + case_ast_node(id, ImportDecl, decl); + // NOTE(bill): Handle later + case_end; + case_ast_node(fsl, ForeignLibrary, decl); + // NOTE(bill): ignore + case_end; + + case_ast_node(cd, ConstDecl, decl); + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + ExactValue v = {ExactValue_Invalid}; + Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v); + e->identifier = name; + DeclInfo *di = make_declaration_info(c->allocator, file_scope); + di->type_expr = cd->type; + di->init_expr = value; + add_entity_and_decl_info(c, name, e, di); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(decl), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(decl), "Extra initial expression"); + } + case_end; + + case_ast_node(vd, VarDecl, decl); + isize entity_count = vd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + DeclInfo *di = NULL; + if (vd->values.count > 0) { + di = make_declaration_info(heap_allocator(), file_scope); + di->entities = entities; + di->entity_count = entity_count; + di->type_expr = vd->type; + di->init_expr = vd->values.e[0]; + } + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + AstNode *value = NULL; + if (i < vd->values.count) { + value = vd->values.e[i]; + } + Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL); + e->identifier = name; + entities[entity_index++] = e; + + DeclInfo *d = di; + if (d == NULL) { + AstNode *init_expr = value; + d = make_declaration_info(heap_allocator(), file_scope); + d->type_expr = vd->type; + d->init_expr = init_expr; + d->var_decl_tags = vd->tags; + } + + add_entity_and_decl_info(c, name, e, d); + } + case_end; + + case_ast_node(td, TypeDecl, decl); + ast_node(n, Ident, td->name); + Entity *e = make_entity_type_name(c->allocator, file_scope, *n, NULL); + e->identifier = td->name; + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = td->type; + add_entity_and_decl_info(c, td->name, e, d); + case_end; + + case_ast_node(pd, ProcDecl, decl); + ast_node(n, Ident, pd->name); + Token token = *n; + Entity *e = make_entity_procedure(c->allocator, file_scope, token, NULL); + e->identifier = pd->name; + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->proc_decl = decl; + add_entity_and_decl_info(c, pd->name, e, d); + case_end; + + default: + error(ast_node_token(decl), "Only declarations are allowed at file scope"); + break; + } + } + } + + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + + for_array(decl_index, f->decls) { + AstNode *decl = f->decls.e[decl_index]; + if (decl->kind != AstNode_ImportDecl) { + continue; + } + ast_node(id, ImportDecl, decl); + + HashKey key = hash_string(id->fullpath); + Scope **found = map_scope_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; + } + + bool previously_added = false; + for_array(import_index, file_scope->imported) { + Scope *prev = file_scope->imported.e[import_index]; + if (prev == scope) { + previously_added = true; + break; + } + } + + if (!previously_added) { + array_add(&file_scope->imported, scope); + } else { + warning(id->token, "Multiple #import of the same file within this scope"); + } + + if (str_eq(id->import_name.string, str_lit("."))) { + // NOTE(bill): Add imported entities to this file's scope + for_array(elem_index, scope->elements.entries) { + Entity *e = scope->elements.entries.e[elem_index].value; + if (e->scope == file_scope) { + continue; + } + + // NOTE(bill): Do not add other imported entities + add_entity(c, file_scope, NULL, e); + if (!id->is_load) { // `#import`ed entities don't get exported + HashKey key = hash_string(e->token.string); + map_entity_set(&file_scope->implicit, key, e); + } + } + } else { + String import_name = id->import_name.string; + if (import_name.len == 0) { + // NOTE(bill): use file name (without extension) as the identifier + // If it is a valid identifier + String filename = id->fullpath; + isize slash = 0; + isize dot = 0; + for (isize i = filename.len-1; i >= 0; i--) { + u8 c = filename.text[i]; + if (c == '/' || c == '\\') { + break; + } + slash = i; + } + + filename.text += slash; + filename.len -= slash; + + dot = filename.len; + while (dot --> 0) { + u8 c = filename.text[dot]; + if (c == '.') { + break; + } + } + + filename.len = dot; + + if (is_string_an_identifier(filename)) { + import_name = filename; + } else { + error(ast_node_token(decl), + "File name, %.*s, cannot be as an import name as it is not a valid identifier", + LIT(filename)); + } + } + + if (import_name.len > 0) { + id->import_name.string = import_name; + Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid, + id->fullpath, id->import_name.string, + scope); + add_entity(c, file_scope, NULL, e); + } + } + } + } + + check_global_entity(c, Entity_TypeName); + + init_preload_types(c); + add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__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.e[i]; + add_curr_ast_file(c, pi->file); + + bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; + bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; + + CheckerContext prev_context = c->context; + + if (bounds_check) { + c->context.stmt_state_flags |= StmtStateFlag_bounds_check; + c->context.stmt_state_flags &= ~StmtStateFlag_no_bounds_check; + } else if (no_bounds_check) { + c->context.stmt_state_flags |= StmtStateFlag_no_bounds_check; + c->context.stmt_state_flags &= ~StmtStateFlag_bounds_check; + } + + check_proc_body(c, pi->token, pi->decl, pi->type, pi->body); + + c->context = prev_context; + } + + // Add untyped expression values + for_array(i, c->info.untyped.entries) { + MapExprInfoEntry *entry = &c->info.untyped.entries.e[i]; + HashKey key = entry->key; + AstNode *expr = cast(AstNode *)cast(uintptr)key.key; + ExprInfo *info = &entry->value; + if (info != NULL && expr != NULL) { + if (is_type_typed(info->type)) { + compiler_error("%s (type %s) is typed!", expr_to_string(expr), type_to_string(info->type)); + } + add_type_and_value(&c->info, expr, info->mode, info->type, info->value); + } + } + + for (isize i = 0; i < gb_count_of(basic_types)-1; i++) { + Type *t = &basic_types[i]; + if (t->Basic.size > 0) { + add_type_info_type(c, t); + } + } + + for (isize i = 0; i < gb_count_of(basic_type_aliases)-1; i++) { + Type *t = &basic_type_aliases[i]; + if (t->Basic.size > 0) { + add_type_info_type(c, t); + } + } + + map_scope_destroy(&file_scopes); + array_free(&import_decls); +} + + + diff --git a/src/checker/decl.c b/src/checker/decl.c new file mode 100644 index 000000000..f5a5daad6 --- /dev/null +++ b/src/checker/decl.c @@ -0,0 +1,545 @@ +bool check_is_terminating(AstNode *node); +void check_stmt (Checker *c, AstNode *node, u32 flags); +void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags); +void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker); +void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr); +void check_proc_decl (Checker *c, Entity *e, DeclInfo *d); +void check_var_decl (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr); + +// NOTE(bill): `content_name` is for debugging and error messages +Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) { + if (operand->mode == Addressing_Invalid || + operand->type == t_invalid || + e->type == t_invalid) { + + if (operand->mode == Addressing_Builtin) { + gbString expr_str = expr_to_string(operand->expr); + + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign builtin procedure `%s` in %.*s", + expr_str, + LIT(context_name)); + + operand->mode = Addressing_Invalid; + + gb_string_free(expr_str); + } + + + if (e->type == NULL) { + e->type = t_invalid; + } + return NULL; + } + + if (e->type == NULL) { + // NOTE(bill): Use the type of the operand + Type *t = operand->type; + if (is_type_untyped(t)) { + if (t == t_invalid || is_type_untyped_nil(t)) { + error(e->token, "Use of untyped nil in %.*s", LIT(context_name)); + e->type = t_invalid; + return NULL; + } + t = default_type(t); + } + e->type = t; + } + + check_assignment(c, operand, e->type, context_name); + if (operand->mode == Addressing_Invalid) { + return NULL; + } + + return e->type; +} + +void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) { + if ((lhs == NULL || lhs_count == 0) && inits.count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be + // an extra allocation + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count); + + for_array(i, inits) { + AstNode *rhs = inits.e[i]; + Operand o = {0}; + check_multi_expr(c, &o, rhs); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + isize rhs_count = operands.count; + for_array(i, operands) { + if (operands.e[i].mode == Addressing_Invalid) { + rhs_count--; + } + } + + + isize max = gb_min(lhs_count, rhs_count); + for (isize i = 0; i < max; i++) { + check_init_variable(c, lhs[i], &operands.e[i], context_name); + } + + if (rhs_count > 0 && lhs_count != rhs_count) { + error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count); + } + + gb_temp_arena_memory_end(tmp); +} + + + +void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) { + if (e->type != NULL) { + return; + } + + if (d == NULL) { + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + if (found) { + d = *found; + } else { + e->type = t_invalid; + set_base_type(named_type, t_invalid); + return; + // GB_PANIC("`%.*s` should been declared!", LIT(e->token.string)); + } + } + + if (e->kind == Entity_Procedure) { + check_proc_decl(c, e, d); + return; + } + CheckerContext prev = c->context; + c->context.scope = d->scope; + c->context.decl = d; + + switch (e->kind) { + case Entity_Constant: + check_const_decl(c, e, d->type_expr, d->init_expr); + break; + case Entity_Variable: + check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr); + break; + case Entity_TypeName: + check_type_decl(c, e, d->type_expr, named_type, cycle_checker); + break; + } + + c->context = prev; +} + + + +void check_var_decl_node(Checker *c, AstNode *node) { + ast_node(vd, VarDecl, node); + isize entity_count = vd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + Entity *entity = NULL; + if (name->kind == AstNode_Ident) { + Token token = name->Ident; + String str = token.string; + Entity *found = NULL; + // NOTE(bill): Ignore assignments to `_` + if (str_ne(str, str_lit("_"))) { + found = current_scope_lookup_entity(c->context.scope, str); + } + if (found == NULL) { + entity = make_entity_variable(c->allocator, c->context.scope, token, NULL); + add_entity_definition(&c->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } else { + error(ast_node_token(name), "A variable declaration must be an identifier"); + } + if (entity == NULL) { + entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + } + entities[entity_index++] = entity; + } + + Type *init_type = NULL; + if (vd->type) { + init_type = check_type_extra(c, vd->type, NULL, NULL); + if (init_type == NULL) + init_type = t_invalid; + } + + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != NULL); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; + } + e->flags |= EntityFlag_Visited; + + if (e->type == NULL) + e->type = init_type; + } + + check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); + + for_array(i, vd->names) { + if (entities[i] != NULL) { + add_entity(c, c->context.scope, vd->names.e[i], entities[i]); + } + } + +} + + + +void check_init_constant(Checker *c, Entity *e, Operand *operand) { + if (operand->mode == Addressing_Invalid || + operand->type == t_invalid || + e->type == t_invalid) { + if (e->type == NULL) { + e->type = t_invalid; + } + return; + } + + if (operand->mode != Addressing_Constant) { + // TODO(bill): better error + error(ast_node_token(operand->expr), + "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string)); + if (e->type == NULL) { + e->type = t_invalid; + } + return; + } + // if (!is_type_constant_type(operand->type)) { + // gbString type_str = type_to_string(operand->type); + // defer (gb_string_free(type_str)); + // error(ast_node_token(operand->expr), + // "Invalid constant type: `%s`", type_str); + // if (e->type == NULL) { + // e->type = t_invalid; + // } + // return; + // } + + if (e->type == NULL) { // NOTE(bill): type inference + e->type = operand->type; + } + + check_assignment(c, operand, e->type, str_lit("constant declaration")); + if (operand->mode == Addressing_Invalid) { + return; + } + + e->Constant.value = operand->value; +} + + +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) { + GB_ASSERT(e->type == NULL); + + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + return; + } + e->flags |= EntityFlag_Visited; + + if (type_expr) { + Type *t = check_type(c, type_expr); + // if (!is_type_constant_type(t)) { + // gbString str = type_to_string(t); + // defer (gb_string_free(str)); + // error(ast_node_token(type_expr), + // "Invalid constant type `%s`", str); + // e->type = t_invalid; + // return; + // } + e->type = t; + } + + Operand operand = {0}; + if (init_expr) { + check_expr(c, &operand, init_expr); + } + check_init_constant(c, e, &operand); +} + +void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) { + GB_ASSERT(e->type == NULL); + Type *named = make_type_named(c->allocator, e->token.string, NULL, e); + named->Named.type_name = e; + if (def != NULL && def->kind == Type_Named) { + def->Named.base = named; + } + e->type = named; + + CycleChecker local_cycle_checker = {0}; + if (cycle_checker == NULL) { + cycle_checker = &local_cycle_checker; + } + + Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e)); + named->Named.base = bt; + named->Named.base = base_type(named->Named.base); + if (named->Named.base == t_invalid) { + gb_printf("check_type_decl: %s\n", type_to_string(named)); + } + + cycle_checker_destroy(&local_cycle_checker); +} + + +bool are_signatures_similar_enough(Type *a_, Type *b_) { + GB_ASSERT(a_->kind == Type_Proc); + GB_ASSERT(b_->kind == Type_Proc); + TypeProc *a = &a_->Proc; + TypeProc *b = &b_->Proc; + + if (a->param_count != b->param_count) { + return false; + } + if (a->result_count != b->result_count) { + return false; + } + for (isize i = 0; i < a->param_count; i++) { + Type *x = base_type(a->params->Tuple.variables[i]->type); + Type *y = base_type(b->params->Tuple.variables[i]->type); + if (is_type_pointer(x) && is_type_pointer(y)) { + continue; + } + + if (!are_types_identical(x, y)) { + return false; + } + } + for (isize i = 0; i < a->result_count; i++) { + Type *x = base_type(a->results->Tuple.variables[i]->type); + Type *y = base_type(b->results->Tuple.variables[i]->type); + if (is_type_pointer(x) && is_type_pointer(y)) { + continue; + } + + if (!are_types_identical(x, y)) { + return false; + } + } + + return true; +} + +void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { + GB_ASSERT(e->type == NULL); + + Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false); + e->type = proc_type; + ast_node(pd, ProcDecl, d->proc_decl); + + check_open_scope(c, pd->type); + check_procedure_type(c, proc_type, pd->type); + + bool is_foreign = (pd->tags & ProcTag_foreign) != 0; + bool is_link_name = (pd->tags & ProcTag_link_name) != 0; + bool is_inline = (pd->tags & ProcTag_inline) != 0; + bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0; + + if ((d->scope->is_file || d->scope->is_global) && + str_eq(e->token.string, str_lit("main"))) { + if (proc_type != NULL) { + TypeProc *pt = &proc_type->Proc; + if (pt->param_count != 0 || + pt->result_count) { + gbString str = type_to_string(proc_type); + error(e->token, + "Procedure type of `main` was expected to be `proc()`, got %s", str); + gb_string_free(str); + } + } + } + + if (is_inline && is_no_inline) { + error(ast_node_token(pd->type), + "You cannot apply both `inline` and `no_inline` to a procedure"); + } + + if (is_foreign && is_link_name) { + error(ast_node_token(pd->type), + "You cannot apply both `foreign` and `link_name` to a procedure"); + } + + if (pd->body != NULL) { + if (is_foreign) { + error(ast_node_token(pd->body), + "A procedure tagged as `#foreign` cannot have a body"); + } + + d->scope = c->context.scope; + + GB_ASSERT(pd->body->kind == AstNode_BlockStmt); + check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags); + } + + if (is_foreign) { + MapEntity *fp = &c->info.foreign_procs; + AstNodeProcDecl *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); + Entity **found = map_entity_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_signatures_similar_enough(this_type, other_type)) { + error(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_entity_set(fp, key, e); + } + } else if (is_link_name) { + MapEntity *fp = &c->info.foreign_procs; + AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl; + String name = proc_decl->link_name; + + HashKey key = hash_string(name); + Entity **found = map_entity_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + error(ast_node_token(d->proc_decl), + "Non unique #link_name for procedure `%.*s`\n" + "\tother at %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } else { + map_entity_set(fp, key, e); + } + } + + check_close_scope(c); +} + +void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) { + GB_ASSERT(e->type == NULL); + GB_ASSERT(e->kind == Entity_Variable); + + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + return; + } + e->flags |= EntityFlag_Visited; + + if (type_expr != NULL) + e->type = check_type_extra(c, type_expr, NULL, NULL); + + if (init_expr == NULL) { + if (type_expr == NULL) + e->type = t_invalid; + return; + } + + if (entities == NULL || entity_count == 1) { + GB_ASSERT(entities == NULL || entities[0] == e); + Operand operand = {0}; + check_expr(c, &operand, init_expr); + check_init_variable(c, e, &operand, str_lit("variable declaration")); + } + + if (type_expr != NULL) { + for (isize i = 0; i < entity_count; i++) + entities[i]->type = e->type; + } + + AstNodeArray inits; + array_init_reserve(&inits, c->allocator, 1); + array_add(&inits, init_expr); + check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration")); +} + +void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) { + GB_ASSERT(body->kind == AstNode_BlockStmt); + + CheckerContext old_context = c->context; + c->context.scope = decl->scope; + c->context.decl = decl; + + GB_ASSERT(type->kind == Type_Proc); + if (type->Proc.param_count > 0) { + TypeTuple *params = &type->Proc.params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (!(e->flags & EntityFlag_Anonymous)) { + continue; + } + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + break; + } + } + } + } else { + error(e->token, "`using` can only be applied to variables of type struct or raw_union"); + break; + } + } + } + + push_procedure(c, type); + { + ast_node(bs, BlockStmt, body); + // TODO(bill): Check declarations first (except mutable variable declarations) + check_stmt_list(c, bs->stmts, 0); + if (type->Proc.result_count > 0) { + if (!check_is_terminating(body)) { + error(bs->close, "Missing return statement at the end of the procedure"); + } + } + } + pop_procedure(c); + + + check_scope_usage(c, c->context.scope); + + c->context = old_context; +} + + + diff --git a/src/checker/entity.c b/src/checker/entity.c new file mode 100644 index 000000000..df1ecf28d --- /dev/null +++ b/src/checker/entity.c @@ -0,0 +1,179 @@ +typedef struct Scope Scope; +typedef struct Checker Checker; +typedef struct Type Type; +typedef enum BuiltinProcId BuiltinProcId; +typedef enum ImplicitValueId ImplicitValueId; + +#define ENTITY_KINDS \ + ENTITY_KIND(Invalid) \ + ENTITY_KIND(Constant) \ + ENTITY_KIND(Variable) \ + ENTITY_KIND(TypeName) \ + ENTITY_KIND(Procedure) \ + ENTITY_KIND(Builtin) \ + ENTITY_KIND(ImportName) \ + ENTITY_KIND(Nil) \ + ENTITY_KIND(ImplicitValue) \ + ENTITY_KIND(Count) + +typedef enum EntityKind { +#define ENTITY_KIND(k) GB_JOIN2(Entity_, k), + ENTITY_KINDS +#undef ENTITY_KIND +} EntityKind; + +String const entity_strings[] = { +#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1}, + ENTITY_KINDS +#undef ENTITY_KIND +}; + +typedef enum EntityFlag { + EntityFlag_Visited = 1<<0, + EntityFlag_Used = 1<<1, + EntityFlag_Anonymous = 1<<2, + EntityFlag_Field = 1<<3, + EntityFlag_Param = 1<<4, + EntityFlag_VectorElem = 1<<5, +} EntityFlag; + +typedef struct Entity Entity; +struct Entity { + EntityKind kind; + u32 flags; + Token token; + Scope * scope; + Type * type; + AstNode * identifier; // Can be NULL + + // TODO(bill): Cleanup how `using` works for entities + Entity * using_parent; + AstNode * using_expr; + + union { + struct { + ExactValue value; + } Constant; + struct { + i32 field_index; + i32 field_src_index; + } Variable; + i32 TypeName; + i32 Procedure; + struct { + BuiltinProcId id; + } Builtin; + struct { + String path; + String name; + Scope *scope; + bool used; + } ImportName; + i32 Nil; + struct { + // TODO(bill): Should this be a user-level construct rather than compiler-level? + ImplicitValueId id; + Entity * backing; + } ImplicitValue; + }; +}; + +Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) { + Entity *entity = gb_alloc_item(a, Entity); + entity->kind = kind; + entity->scope = scope; + entity->token = token; + entity->type = type; + return entity; +} + +Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) { + Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type); + return entity; +} + +Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) { + GB_ASSERT(parent != NULL); + Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type); + entity->using_parent = parent; + entity->flags |= EntityFlag_Anonymous; + return entity; +} + + +Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) { + Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type); + entity->Constant.value = value; + return entity; +} + +Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) { + Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type); + return entity; +} + +Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->flags |= EntityFlag_Used; + entity->flags |= EntityFlag_Anonymous*(anonymous != 0); + entity->flags |= EntityFlag_Param; + return entity; +} + +Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->Variable.field_src_index = field_src_index; + entity->Variable.field_index = field_src_index; + entity->flags |= EntityFlag_Field; + entity->flags |= EntityFlag_Anonymous*(anonymous != 0); + return entity; +} + +Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->Variable.field_src_index = field_src_index; + entity->Variable.field_index = field_src_index; + entity->flags |= EntityFlag_Field; + entity->flags |= EntityFlag_VectorElem; + return entity; +} + +Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) { + Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type); + return entity; +} + +Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) { + Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type); + entity->Builtin.id = id; + return entity; +} + +Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type, + String path, String name, Scope *import_scope) { + Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type); + entity->ImportName.path = path; + entity->ImportName.name = name; + entity->ImportName.scope = import_scope; + return entity; +} + +Entity *make_entity_nil(gbAllocator a, String name, Type *type) { + Token token = make_token_ident(name); + Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type); + return entity; +} + +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; +} + + +Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) { + token.string = str_lit("_"); + return make_entity_variable(a, file_scope, token, NULL); +} + diff --git a/src/checker/expr.c b/src/checker/expr.c new file mode 100644 index 000000000..6f16da451 --- /dev/null +++ b/src/checker/expr.c @@ -0,0 +1,4465 @@ +void check_expr (Checker *c, Operand *operand, AstNode *expression); +void check_multi_expr (Checker *c, Operand *operand, AstNode *expression); +void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression); +ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint); +Type * check_type_extra (Checker *c, AstNode *expression, Type *named_type, CycleChecker *cycle_checker); +Type * check_type (Checker *c, AstNode *expression); +void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker); +Entity * check_selector (Checker *c, Operand *operand, AstNode *node); +void check_not_tuple (Checker *c, Operand *operand); +bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value); +void convert_to_typed (Checker *c, Operand *operand, Type *target_type, i32 level); +gbString expr_to_string (AstNode *expression); +void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker); +void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body); +void update_expr_type (Checker *c, AstNode *e, Type *type, bool final); + +gb_inline Type *check_type(Checker *c, AstNode *expression) { + return check_type_extra(c, expression, NULL, NULL); +} + + + +bool check_is_assignable_to_using_subtype(Type *dst, Type *src) { + Type *prev_src = src; + // Type *prev_dst = dst; + src = base_type(type_deref(src)); + // dst = base_type(type_deref(dst)); + bool src_is_ptr = src != prev_src; + // bool dst_is_ptr = dst != prev_dst; + + if (is_type_struct(src)) { + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind == Entity_Variable && (f->flags & EntityFlag_Anonymous)) { + if (are_types_identical(dst, f->type)) { + return true; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(type_deref(dst), f->type)) { + return true; + } + } + bool ok = check_is_assignable_to_using_subtype(dst, f->type); + if (ok) { + return true; + } + } + } + } + return false; +} + + +bool check_is_assignable_to(Checker *c, Operand *operand, Type *type) { + if (operand->mode == Addressing_Invalid || + type == t_invalid) { + return true; + } + + if (operand->mode == Addressing_Builtin) { + return false; + } + + Type *s = operand->type; + + if (are_types_identical(s, type)) { + return true; + } + + Type *src = base_type(s); + Type *dst = base_type(type); + + if (is_type_untyped(src)) { + switch (dst->kind) { + case Type_Basic: + if (operand->mode == Addressing_Constant) { + return check_value_is_expressible(c, operand->value, dst, NULL); + } + if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) { + return is_type_boolean(dst); + } + break; + } + if (type_has_nil(dst)) { + return operand->mode == Addressing_Value && operand->type == t_untyped_nil; + } + } + + if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) { + if (is_type_enum(dst) && is_type_enum(src)) { + return are_types_identical(s, type); + } + return true; + } + + if (is_type_maybe(dst)) { + Type *elem = base_type(dst)->Maybe.elem; + return are_types_identical(elem, s); + } + + if (is_type_untyped_nil(src)) { + return type_has_nil(dst); + } + + // ^T <- rawptr + // TODO(bill): Should C-style (not C++) pointer cast be allowed? + // if (is_type_pointer(dst) && is_type_rawptr(src)) { + // return true; + // } + + // rawptr <- ^T + if (is_type_rawptr(dst) && is_type_pointer(src)) { + return true; + } + + + + if (dst->kind == Type_Array && src->kind == Type_Array) { + if (are_types_identical(dst->Array.elem, src->Array.elem)) { + return dst->Array.count == src->Array.count; + } + } + + if (dst->kind == Type_Slice && src->kind == Type_Slice) { + if (are_types_identical(dst->Slice.elem, src->Slice.elem)) { + return true; + } + } + + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, s)) { + return true; + } + } + } + + + if (dst == t_any) { + // NOTE(bill): Anything can cast to `Any` + add_type_info_type(c, s); + return true; + } + + return false; +} + + +// NOTE(bill): `content_name` is for debugging and error messages +void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) { + check_not_tuple(c, operand); + if (operand->mode == Addressing_Invalid) { + return; + } + + if (is_type_untyped(operand->type)) { + Type *target_type = type; + + if (type == NULL || is_type_any(type) || is_type_untyped_nil(type)) { + if (type == NULL && base_type(operand->type) == t_untyped_nil) { + error(ast_node_token(operand->expr), "Use of untyped nil in %.*s", LIT(context_name)); + operand->mode = Addressing_Invalid; + return; + } + + add_type_info_type(c, type); + target_type = default_type(operand->type); + } + convert_to_typed(c, operand, target_type, 0); + if (operand->mode == Addressing_Invalid) { + return; + } + } + + if (type != NULL) { + if (!check_is_assignable_to(c, operand, type)) { + gbString type_str = type_to_string(type); + gbString op_type_str = type_to_string(operand->type); + gbString expr_str = expr_to_string(operand->expr); + + if (operand->mode == Addressing_Builtin) { + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign builtin procedure `%s` in %.*s", + expr_str, + LIT(context_name)); + } else { + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign value `%s` of type `%s` to `%s` in %.*s", + expr_str, + op_type_str, + type_str, + LIT(context_name)); + } + operand->mode = Addressing_Invalid; + + gb_string_free(expr_str); + gb_string_free(op_type_str); + gb_string_free(type_str); + return; + } + } +} + + +void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *entity_map) { + t = base_type(type_deref(t)); + gbString str = expr_to_string(node); + + if (t->kind == Type_Record) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + HashKey key = hash_string(name); + Entity **found = map_entity_get(entity_map, key); + if (found != NULL) { + Entity *e = *found; + // TODO(bill): Better type error + error(e->token, "`%.*s` is already declared in `%s`", LIT(name), str); + } else { + map_entity_set(entity_map, key, f); + add_entity(c, c->context.scope, NULL, f); + if (f->flags & EntityFlag_Anonymous) { + populate_using_entity_map(c, node, f->type, entity_map); + } + } + } + } + + gb_string_free(str); +} + +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr); + +void check_fields(Checker *c, AstNode *node, AstNodeArray decls, + Entity **fields, isize field_count, + Entity **other_fields, isize other_field_count, + CycleChecker *cycle_checker, String context) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + MapEntity entity_map = {0}; + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(field_count+other_field_count)); + + isize other_field_index = 0; + Entity *using_index_expr = NULL; + + + typedef struct { + Entity *e; + AstNode *t; + } Delay; + Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count); + Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, other_field_count); + + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind == AstNode_ConstDecl) { + ast_node(cd, ConstDecl, decl); + + isize entity_count = cd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + + GB_ASSERT(name->kind == AstNode_Ident); + ExactValue v = {ExactValue_Invalid}; + Token name_token = name->Ident; + Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v); + entities[entity_index++] = e; + + Delay delay = {e, cd->type}; + array_add(&delayed_const, delay); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + // TODO(bill): Better error messages or is this good enough? + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(node), "Extra initial expression"); + } + + for_array(i, cd->names) { + AstNode *name = cd->names.e[i]; + Entity *e = entities[i]; + Token name_token = name->Ident; + if (str_eq(name_token.string, str_lit("_"))) { + other_fields[other_field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity(c, c->context.scope, name, e); + } + } + } else if (decl->kind == AstNode_TypeDecl) { + ast_node(td, TypeDecl, decl); + Token name_token = td->name->Ident; + + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL); + Delay delay = {e, td->type}; + array_add(&delayed_type, delay); + + if (str_eq(name_token.string, str_lit("_"))) { + other_fields[other_field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity(c, c->context.scope, td->name, e); + add_entity_use(c, td->name, e); + } + } + } + + for_array(i, delayed_type) { + check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL); + } + for_array(i, delayed_const) { + check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL); + } + + if (node->kind == AstNode_UnionType) { + isize field_index = 0; + fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind != AstNode_VarDecl) { + continue; + } + + ast_node(vd, VarDecl, decl); + Type *base_type = check_type_extra(c, vd->type, NULL, cycle_checker); + + for_array(name_index, vd->names) { + AstNode *name = vd->names.e[name_index]; + 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); + + if (str_eq(name_token.string, str_lit("_"))) { + error(name_token, "`_` cannot be used a union subtype"); + continue; + } + + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + fields[field_index++] = e; + } + add_entity_use(c, name, e); + } + } + } else { + isize field_index = 0; + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind != AstNode_VarDecl) { + continue; + } + ast_node(vd, VarDecl, decl); + + Type *type = check_type_extra(c, vd->type, NULL, cycle_checker); + + if (vd->is_using) { + if (vd->names.count > 1) { + error(ast_node_token(vd->names.e[0]), + "Cannot apply `using` to more than one of the same type"); + } + } + + for_array(name_index, vd->names) { + AstNode *name = vd->names.e[name_index]; + Token name_token = name->Ident; + + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using, cast(i32)field_index); + e->identifier = name; + if (str_eq(name_token.string, str_lit("_"))) { + fields[field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + fields[field_index++] = e; + add_entity(c, c->context.scope, name, e); + } + add_entity_use(c, name, e); + } + } + + + if (vd->is_using) { + Type *t = base_type(type_deref(type)); + if (!is_type_struct(t) && !is_type_raw_union(t)) { + Token name_token = vd->names.e[0]->Ident; + if (is_type_indexable(t)) { + bool ok = true; + for_array(emi, entity_map.entries) { + Entity *e = entity_map.entries.e[emi].value; + if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + if (is_type_indexable(e->type)) { + if (e->identifier != vd->names.e[0]) { + ok = false; + using_index_expr = e; + break; + } + } + } + } + if (ok) { + using_index_expr = fields[field_index-1]; + } else { + fields[field_index-1]->flags &= ~EntityFlag_Anonymous; + error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string)); + } + } else { + error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string)); + continue; + } + } + + populate_using_entity_map(c, node, type, &entity_map); + } + } + } + + gb_temp_arena_memory_end(tmp); +} + + +// TODO(bill): Cleanup struct field reordering +// TODO(bill): Inline sorting procedure? +gb_global BaseTypeSizes __checker_sizes = {0}; +gb_global gbAllocator __checker_allocator = {0}; + +GB_COMPARE_PROC(cmp_struct_entity_size) { + // Rule: + // Biggest to smallest alignment + // if same alignment: biggest to smallest size + // if same size: order by source order + Entity *x = *(Entity **)a; + Entity *y = *(Entity **)b; + GB_ASSERT(x != NULL); + GB_ASSERT(y != NULL); + GB_ASSERT(x->kind == Entity_Variable); + GB_ASSERT(y->kind == Entity_Variable); + i64 xa = type_align_of(__checker_sizes, __checker_allocator, x->type); + i64 ya = type_align_of(__checker_sizes, __checker_allocator, y->type); + i64 xs = type_size_of(__checker_sizes, __checker_allocator, x->type); + i64 ys = type_size_of(__checker_sizes, __checker_allocator, y->type); + + if (xa == ya) { + if (xs == ys) { + i32 diff = x->Variable.field_index - y->Variable.field_index; + return diff < 0 ? -1 : diff > 0; + } + return xs > ys ? -1 : xs < ys; + } + return xa > ya ? -1 : xa < ya; +} + +void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(is_type_struct(struct_type)); + ast_node(st, StructType, node); + + isize field_count = 0; + isize other_field_count = 0; + for_array(decl_index, st->decls) { + AstNode *decl = st->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, st->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("struct")); + + + struct_type->Record.struct_is_packed = st->is_packed; + struct_type->Record.struct_is_ordered = st->is_ordered; + struct_type->Record.fields = fields; + struct_type->Record.fields_in_src_order = fields; + struct_type->Record.field_count = field_count; + struct_type->Record.other_fields = other_fields; + struct_type->Record.other_field_count = other_field_count; + + + + if (!st->is_packed && !st->is_ordered) { + // NOTE(bill): Reorder fields for reduced size/performance + + Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, field_count); + for (isize i = 0; i < field_count; i++) { + reordered_fields[i] = struct_type->Record.fields_in_src_order[i]; + } + + // NOTE(bill): Hacky thing + // TODO(bill): Probably make an inline sorting procedure rather than use global variables + __checker_sizes = c->sizes; + __checker_allocator = c->allocator; + // NOTE(bill): compound literal order must match source not layout + gb_sort_array(reordered_fields, field_count, cmp_struct_entity_size); + + for (isize i = 0; i < field_count; i++) { + reordered_fields[i]->Variable.field_index = i; + } + + struct_type->Record.fields = reordered_fields; + } + + type_set_offsets(c->sizes, c->allocator, struct_type); +} + +void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(is_type_union(union_type)); + ast_node(ut, UnionType, node); + + isize field_count = 1; + isize other_field_count = 0; + for_array(decl_index, ut->decls) { + AstNode *decl = ut->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; +} + +void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(node->kind == AstNode_RawUnionType); + GB_ASSERT(is_type_raw_union(union_type)); + ast_node(ut, RawUnionType, node); + + isize field_count = 0; + isize other_field_count = 0; + for_array(decl_index, ut->decls) { + AstNode *decl = ut->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("raw union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; +} + +GB_COMPARE_PROC(cmp_enum_order) { + // Rule: + // Biggest to smallest alignment + // if same alignment: biggest to smallest size + // if same size: order by source order + Entity *x = *(Entity **)a; + Entity *y = *(Entity **)b; + GB_ASSERT(x != NULL); + GB_ASSERT(y != NULL); + GB_ASSERT(x->kind == Entity_Constant); + GB_ASSERT(y->kind == Entity_Constant); + GB_ASSERT(x->Constant.value.kind == ExactValue_Integer); + GB_ASSERT(y->Constant.value.kind == ExactValue_Integer); + i64 i = x->Constant.value.value_integer; + i64 j = y->Constant.value.value_integer; + + return i < j ? -1 : i > j; +} + + + +void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) { + GB_ASSERT(node->kind == AstNode_EnumType); + GB_ASSERT(is_type_enum(enum_type)); + ast_node(et, EnumType, node); + + + + Type *base_type = t_int; + if (et->base_type != NULL) { + base_type = check_type(c, et->base_type); + } + + if (base_type == NULL || !is_type_integer(base_type)) { + error(et->token, "Base type for enumeration must be an integer"); + return; + } else + if (base_type == NULL) { + base_type = t_int; + } + enum_type->Record.enum_base = base_type; + + Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count); + isize field_index = 0; + ExactValue iota = make_exact_value_integer(-1); + i64 min_value = 0; + i64 max_value = 0; + + Type *constant_type = enum_type; + if (named_type != NULL) { + constant_type = named_type; + } + + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + MapEntity entity_map = {0}; + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count)); + + Entity *blank_entity = make_entity_constant(c->allocator, c->context.scope, blank_token, constant_type, make_exact_value_integer(0));; + + for_array(i, et->fields) { + AstNode *field = et->fields.e[i]; + + ast_node(f, FieldValue, field); + Token name_token = f->field->Ident; + + if (str_eq(name_token.string, str_lit("count"))) { + error(name_token, "`count` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } else if (str_eq(name_token.string, str_lit("min_value"))) { + error(name_token, "`min_value` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } else if (str_eq(name_token.string, str_lit("max_value"))) { + error(name_token, "`max_value` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } + + Operand o = {0}; + if (f->value != NULL) { + check_expr(c, &o, f->value); + if (o.mode != Addressing_Constant) { + error(ast_node_token(f->value), "Enumeration value must be a constant integer"); + o.mode = Addressing_Invalid; + } + if (o.mode != Addressing_Invalid) { + check_assignment(c, &o, constant_type, str_lit("enumeration")); + } + if (o.mode != Addressing_Invalid) { + iota = o.value; + } else { + Token add_token = {Token_Add}; + iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1)); + } + } else { + Token add_token = {Token_Add}; + iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1)); + } + + + Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, constant_type, iota); + if (min_value > iota.value_integer) { + min_value = iota.value_integer; + } + if (max_value < iota.value_integer) { + max_value = iota.value_integer; + } + + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key)) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this enumeration", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + add_entity(c, c->context.scope, NULL, e); + fields[field_index++] = e; + } + add_entity_use(c, f->field, e); + } + + GB_ASSERT(field_index <= et->fields.count); + + gb_sort_array(fields, field_index, cmp_enum_order); + + enum_type->Record.other_fields = fields; + enum_type->Record.other_field_count = field_index; + + enum_type->Record.enum_count = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("count")), t_int, make_exact_value_integer(enum_type->Record.other_field_count)); + enum_type->Record.min_value = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("min_value")), constant_type, make_exact_value_integer(min_value)); + enum_type->Record.max_value = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("max_value")), constant_type, make_exact_value_integer(max_value)); + + gb_temp_arena_memory_end(tmp); +} + +Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) { + if (params.count == 0) { + return NULL; + } + + bool is_variadic = false; + + Type *tuple = make_type_tuple(c->allocator); + + isize variable_count = 0; + for_array(i, params) { + AstNode *field = params.e[i]; + ast_node(p, Parameter, field); + variable_count += p->names.count; + } + + Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count); + isize variable_index = 0; + for_array(i, params) { + ast_node(p, Parameter, params.e[i]); + AstNode *type_expr = p->type; + if (type_expr) { + if (type_expr->kind == AstNode_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + if (i+1 == params.count) { + is_variadic = true; + } else { + error(ast_node_token(params.e[i]), "Invalid AST: Invalid variadic parameter"); + } + } + + Type *type = check_type(c, type_expr); + for_array(j, p->names) { + AstNode *name = p->names.e[j]; + if (name->kind == AstNode_Ident) { + Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->is_using); + add_entity(c, scope, name, param); + variables[variable_index++] = param; + } else { + error(ast_node_token(name), "Invalid AST: Invalid parameter"); + } + } + } + } + + variable_count = variable_index; + + if (is_variadic) { + GB_ASSERT(params.count > 0); + // NOTE(bill): Change last variadic parameter to be a slice + // Custom Calling convention for variadic parameters + Entity *end = variables[variable_count-1]; + end->type = make_type_slice(c->allocator, end->type); + } + + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = variable_count; + + if (is_variadic_) *is_variadic_ = is_variadic; + + return tuple; +} + +Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) { + if (results.count == 0) { + return NULL; + } + Type *tuple = make_type_tuple(c->allocator); + + Entity **variables = gb_alloc_array(c->allocator, Entity *, results.count); + isize variable_index = 0; + for_array(i, results) { + AstNode *item = results.e[i]; + Type *type = check_type(c, item); + Token token = ast_node_token(item); + token.string = str_lit(""); // NOTE(bill): results are not named + // TODO(bill): Should I have named results? + Entity *param = make_entity_param(c->allocator, scope, token, type, false); + // NOTE(bill): No need to record + variables[variable_index++] = param; + } + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = results.count; + + return tuple; +} + + +void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { + ast_node(pt, ProcType, proc_type_node); + + bool variadic = false; + Type *params = check_get_params(c, c->context.scope, pt->params, &variadic); + Type *results = check_get_results(c, c->context.scope, pt->results); + + isize param_count = 0; + isize result_count = 0; + if (params) param_count = params ->Tuple.variable_count; + if (results) result_count = results->Tuple.variable_count; + + + type->Proc.scope = c->context.scope; + type->Proc.params = params; + type->Proc.param_count = param_count; + type->Proc.results = results; + type->Proc.result_count = result_count; + type->Proc.variadic = variadic; + // type->Proc.implicit_context = implicit_context; +} + + +void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, CycleChecker *cycle_checker) { + GB_ASSERT(n->kind == AstNode_Ident); + o->mode = Addressing_Invalid; + o->expr = n; + Entity *e = scope_lookup_entity(c->context.scope, n->Ident.string); + if (e == NULL) { + if (str_eq(n->Ident.string, str_lit("_"))) { + error(n->Ident, "`_` cannot be used as a value type"); + } else { + error(n->Ident, "Undeclared name: %.*s", LIT(n->Ident.string)); + } + o->type = t_invalid; + o->mode = Addressing_Invalid; + if (named_type != NULL) { + set_base_type(named_type, t_invalid); + } + return; + } + add_entity_use(c, n, e); + + // CycleChecker local_cycle_checker = {0}; + // if (cycle_checker == NULL) { + // cycle_checker = &local_cycle_checker; + // } + // defer (cycle_checker_destroy(&local_cycle_checker)); + + check_entity_decl(c, e, NULL, named_type, cycle_checker); + + if (e->type == NULL) { + compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string)); + return; + } + + Type *type = e->type; + + switch (e->kind) { + case Entity_Constant: + if (type == t_invalid) { + o->type = t_invalid; + return; + } + o->value = e->Constant.value; + GB_ASSERT(o->value.kind != ExactValue_Invalid); + o->mode = Addressing_Constant; + break; + + case Entity_Variable: + e->flags |= EntityFlag_Used; + if (type == t_invalid) { + o->type = t_invalid; + return; + } + #if 0 + if (e->Variable.param) { + o->mode = Addressing_Value; + } else { + o->mode = Addressing_Variable; + } + #else + o->mode = Addressing_Variable; + #endif + break; + + case Entity_TypeName: { + o->mode = Addressing_Type; +#if 0 + // TODO(bill): Fix cyclical dependancy checker + if (cycle_checker != NULL) { + for_array(i, cycle_checker->path) { + Entity *prev = cycle_checker->path[i]; + if (prev == e) { + error(e->token, "Illegal declaration cycle for %.*s", LIT(e->token.string)); + for (isize j = i; j < gb_array_count(cycle_checker->path); j++) { + Entity *ref = cycle_checker->path[j]; + error(ref->token, "\t%.*s refers to", LIT(ref->token.string)); + } + error(e->token, "\t%.*s", LIT(e->token.string)); + type = t_invalid; + break; + } + } + } +#endif + } break; + + case Entity_Procedure: + o->mode = Addressing_Value; + break; + + case Entity_Builtin: + o->builtin_id = e->Builtin.id; + o->mode = Addressing_Builtin; + break; + + case Entity_ImportName: + error(ast_node_token(n), "Use of import `%.*s` not in selector", LIT(e->ImportName.name)); + return; + + case Entity_Nil: + o->mode = Addressing_Value; + break; + + case Entity_ImplicitValue: + o->mode = Addressing_Value; + break; + + default: + compiler_error("Compiler error: Unknown EntityKind"); + break; + } + + o->type = type; +} + +i64 check_array_count(Checker *c, AstNode *e) { + if (e == NULL) { + return 0; + } + Operand o = {0}; + check_expr(c, &o, e); + if (o.mode != Addressing_Constant) { + if (o.mode != Addressing_Invalid) { + error(ast_node_token(e), "Array count must be a constant"); + } + return 0; + } + if (is_type_untyped(o.type) || is_type_integer(o.type)) { + if (o.value.kind == ExactValue_Integer) { + i64 count = o.value.value_integer; + if (count >= 0) { + return count; + } + error(ast_node_token(e), "Invalid array count"); + return 0; + } + } + + error(ast_node_token(e), "Array count must be an integer"); + return 0; +} + +Type *check_type_extra(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_checker) { + ExactValue null_value = {ExactValue_Invalid}; + Type *type = NULL; + gbString err_str = NULL; + + switch (e->kind) { + case_ast_node(i, Ident, e); + Operand o = {0}; + check_identifier(c, &o, e, named_type, cycle_checker); + + switch (o.mode) { + case Addressing_Invalid: + break; + case Addressing_Type: { + type = o.type; + goto end; + } break; + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type", err_str); + break; + default: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type when not a type", err_str); + break; + } + case_end; + + case_ast_node(se, SelectorExpr, e); + Operand o = {0}; + check_selector(c, &o, e); + + switch (o.mode) { + case Addressing_Invalid: + break; + case Addressing_Type: + GB_ASSERT(o.type != NULL); + type = o.type; + goto end; + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type", err_str); + break; + default: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not a type", err_str); + break; + } + case_end; + + case_ast_node(pe, ParenExpr, e); + type = check_type_extra(c, pe->expr, named_type, cycle_checker); + goto end; + case_end; + + case_ast_node(ue, UnaryExpr, e); + if (ue->op.kind == Token_Pointer) { + type = make_type_pointer(c->allocator, check_type(c, ue->expr)); + goto end; + } else if (ue->op.kind == Token_Maybe) { + type = make_type_maybe(c->allocator, check_type(c, ue->expr)); + goto end; + } + case_end; + + case_ast_node(pt, PointerType, e); + Type *elem = check_type(c, pt->type); + type = make_type_pointer(c->allocator, elem); + goto end; + case_end; + + case_ast_node(mt, MaybeType, e); + Type *elem = check_type(c, mt->type); + type = make_type_maybe(c->allocator, elem); + goto end; + case_end; + + case_ast_node(at, ArrayType, e); + if (at->count != NULL) { + Type *elem = check_type_extra(c, at->elem, NULL, cycle_checker); + type = make_type_array(c->allocator, elem, check_array_count(c, at->count)); + } else { + Type *elem = check_type(c, at->elem); + type = make_type_slice(c->allocator, elem); + } + goto end; + case_end; + + + case_ast_node(vt, VectorType, e); + Type *elem = check_type(c, vt->elem); + Type *be = base_type(elem); + i64 count = check_array_count(c, vt->count); + if (!is_type_boolean(be) && !is_type_numeric(be)) { + err_str = type_to_string(elem); + error(ast_node_token(vt->elem), "Vector element type must be numerical or a boolean. Got `%s`", err_str); + } + type = make_type_vector(c->allocator, elem, count); + goto end; + case_end; + + case_ast_node(st, StructType, e); + type = make_type_struct(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_struct_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(ut, UnionType, e); + type = make_type_union(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_union_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(rut, RawUnionType, e); + type = make_type_raw_union(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_raw_union_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(et, EnumType, e); + type = make_type_enum(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_enum_type(c, type, named_type, e); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(pt, ProcType, e); + type = alloc_type(c->allocator, Type_Proc); + set_base_type(named_type, type); + check_open_scope(c, e); + check_procedure_type(c, type, e); + check_close_scope(c); + goto end; + case_end; + + case_ast_node(ce, CallExpr, e); + Operand o = {0}; + check_expr_or_type(c, &o, e); + if (o.mode == Addressing_Type) { + type = o.type; + goto end; + } + case_end; + } + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not a type", err_str); + + type = t_invalid; +end: + gb_string_free(err_str); + + if (type == NULL) { + type = t_invalid; + } + + set_base_type(named_type, type); + GB_ASSERT(is_type_typed(type)); + + add_type_and_value(&c->info, e, Addressing_Type, type, null_value); + + + return type; +} + + +bool check_unary_op(Checker *c, Operand *o, Token op) { + // TODO(bill): Handle errors correctly + Type *type = base_type(base_vector_type(o->type)); + gbString str = NULL; + switch (op.kind) { + case Token_Add: + case Token_Sub: + if (!is_type_numeric(type)) { + str = expr_to_string(o->expr); + error(op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str); + gb_string_free(str); + } + break; + + case Token_Xor: + if (!is_type_integer(type)) { + error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + } + break; + + case Token_Not: + if (!is_type_boolean(type)) { + str = expr_to_string(o->expr); + error(op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string)); + gb_string_free(str); + } + break; + + default: + error(op, "Unknown operator `%.*s`", LIT(op.string)); + return false; + } + + return true; +} + +bool check_binary_op(Checker *c, Operand *o, Token op) { + // TODO(bill): Handle errors correctly + Type *type = base_type(base_vector_type(o->type)); + switch (op.kind) { + case Token_Sub: + case Token_SubEq: + if (!is_type_numeric(type) && !is_type_pointer(type)) { + error(op, "Operator `%.*s` is only allowed with numeric or pointer expressions", LIT(op.string)); + return false; + } + if (is_type_pointer(type)) { + o->type = t_int; + } + if (base_type(type) == t_rawptr) { + gbString str = type_to_string(type); + error(ast_node_token(o->expr), "Invalid pointer type for pointer arithmetic: `%s`", str); + gb_string_free(str); + return false; + } + break; + + case Token_Add: + case Token_Mul: + case Token_Quo: + case Token_AddEq: + case Token_MulEq: + case Token_QuoEq: + if (!is_type_numeric(type)) { + error(op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string)); + return false; + } + break; + + case Token_And: + case Token_Or: + case Token_AndEq: + case Token_OrEq: + if (!is_type_integer(type) && !is_type_boolean(type)) { + error(op, "Operator `%.*s` is only allowed with integers or booleans", LIT(op.string)); + return false; + } + break; + + case Token_Mod: + case Token_Xor: + case Token_AndNot: + case Token_ModEq: + case Token_XorEq: + case Token_AndNotEq: + if (!is_type_integer(type)) { + error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + return false; + } + break; + + case Token_CmpAnd: + case Token_CmpOr: + + case Token_CmpAndEq: + case Token_CmpOrEq: + if (!is_type_boolean(type)) { + error(op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string)); + return false; + } + break; + + default: + error(op, "Unknown operator `%.*s`", LIT(op.string)); + return false; + } + + return true; + +} +bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) { + if (in_value.kind == ExactValue_Invalid) { + // NOTE(bill): There's already been an error + return true; + } + + if (is_type_boolean(type)) { + return in_value.kind == ExactValue_Bool; + } else if (is_type_string(type)) { + return in_value.kind == ExactValue_String; + } else if (is_type_integer(type)) { + ExactValue v = exact_value_to_integer(in_value); + if (v.kind != ExactValue_Integer) { + return false; + } + if (out_value) *out_value = v; + i64 i = v.value_integer; + u64 u = *cast(u64 *)&i; + i64 s = 8*type_size_of(c->sizes, c->allocator, type); + u64 umax = ~0ull; + if (s < 64) { + umax = (1ull << s) - 1ull; + } else { + // TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats + s = 64; + } + i64 imax = (1ll << (s-1ll)); + + + switch (type->Basic.kind) { + case Basic_i8: + case Basic_i16: + case Basic_i32: + case Basic_i64: + case Basic_i128: + case Basic_int: + return gb_is_between(i, -imax, imax-1); + + case Basic_u8: + case Basic_u16: + case Basic_u32: + case Basic_u64: + case Basic_u128: + case Basic_uint: + return !(u < 0 || u > umax); + + case Basic_UntypedInteger: + return true; + + default: GB_PANIC("Compiler error: Unknown integer type!"); break; + } + } else if (is_type_float(type)) { + ExactValue v = exact_value_to_float(in_value); + if (v.kind != ExactValue_Float) { + return false; + } + + switch (type->Basic.kind) { + // case Basic_f16: + case Basic_f32: + case Basic_f64: + // case Basic_f128: + if (out_value) *out_value = v; + return true; + + case Basic_UntypedFloat: + return true; + } + } else if (is_type_pointer(type)) { + if (in_value.kind == ExactValue_Pointer) { + return true; + } + if (in_value.kind == ExactValue_Integer) { + return true; + } + if (out_value) *out_value = in_value; + } + + + return false; +} + +void check_is_expressible(Checker *c, Operand *o, Type *type) { + GB_ASSERT(type->kind == Type_Basic); + GB_ASSERT(o->mode == Addressing_Constant); + if (!check_value_is_expressible(c, o->value, type, &o->value)) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + if (is_type_numeric(o->type) && is_type_numeric(type)) { + if (!is_type_integer(o->type) && is_type_integer(type)) { + error(ast_node_token(o->expr), "`%s` truncated to `%s`", a, b); + } else { + error(ast_node_token(o->expr), "`%s = %lld` overflows `%s`", a, o->value.value_integer, b); + } + } else { + error(ast_node_token(o->expr), "Cannot convert `%s` to `%s`", a, b); + } + + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + } +} + +bool check_is_expr_vector_index(Checker *c, AstNode *expr) { + // HACK(bill): Handle this correctly. Maybe with a custom AddressingMode + expr = unparen_expr(expr); + if (expr->kind == AstNode_IndexExpr) { + ast_node(ie, IndexExpr, expr); + Type *t = type_deref(type_of_expr(&c->info, ie->expr)); + if (t != NULL) { + return is_type_vector(t); + } + } + return false; +} + +bool check_is_vector_elem(Checker *c, AstNode *expr) { + // HACK(bill): Handle this correctly. Maybe with a custom AddressingMode + expr = unparen_expr(expr); + if (expr->kind == AstNode_SelectorExpr) { + ast_node(se, SelectorExpr, expr); + Type *t = type_deref(type_of_expr(&c->info, se->expr)); + if (t != NULL && is_type_vector(t)) { + return true; + } + } + return false; +} + +void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) { + switch (op.kind) { + case Token_Pointer: { // Pointer address + if (o->mode != Addressing_Variable || + check_is_expr_vector_index(c, o->expr) || + check_is_vector_elem(c, o->expr)) { + ast_node(ue, UnaryExpr, node); + gbString str = expr_to_string(ue->expr); + error(op, "Cannot take the pointer address of `%s`", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + return; + } + o->mode = Addressing_Value; + o->type = make_type_pointer(c->allocator, o->type); + return; + } + + case Token_Maybe: { // Make maybe + Type *t = default_type(o->type); + bool is_value = + o->mode == Addressing_Variable || + o->mode == Addressing_Value || + o->mode == Addressing_Constant; + + if (!is_value || is_type_untyped(t)) { + ast_node(ue, UnaryExpr, node); + gbString str = expr_to_string(ue->expr); + error(op, "Cannot convert `%s` to a maybe", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + return; + } + o->mode = Addressing_Value; + o->type = make_type_maybe(c->allocator, t); + return; + } + } + + if (!check_unary_op(c, o, op)) { + o->mode = Addressing_Invalid; + return; + } + + if (o->mode == Addressing_Constant) { + Type *type = base_type(o->type); + if (type->kind != Type_Basic) { + gbString xt = type_to_string(o->type); + gbString err_str = expr_to_string(node); + error(op, "Invalid type, `%s`, for constant unary expression `%s`", xt, err_str); + gb_string_free(err_str); + gb_string_free(xt); + o->mode = Addressing_Invalid; + return; + } + + + i32 precision = 0; + if (is_type_unsigned(type)) { + precision = cast(i32)(8 * type_size_of(c->sizes, c->allocator, type)); + } + o->value = exact_unary_operator_value(op, o->value, precision); + + if (is_type_typed(type)) { + if (node != NULL) { + o->expr = node; + } + check_is_expressible(c, o, type); + } + return; + } + + o->mode = Addressing_Value; +} + +void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + gbString err_str = NULL; + + if (check_is_assignable_to(c, x, y->type) || + check_is_assignable_to(c, y, x->type)) { + Type *err_type = x->type; + bool defined = false; + switch (op.kind) { + case Token_CmpEq: + case Token_NotEq: + defined = is_type_comparable(x->type); + break; + case Token_Lt: + case Token_Gt: + case Token_LtEq: + case Token_GtEq: { + defined = is_type_ordered(x->type); + } break; + } + + // CLEANUP(bill) NOTE(bill): there is an auto assignment to `any` which needs to be checked + if (is_type_any(x->type) && !is_type_any(y->type)) { + err_type = x->type; + defined = false; + } else if (is_type_any(y->type) && !is_type_any(x->type)) { + err_type = y->type; + defined = false; + } + + if (!defined) { + gbString type_string = type_to_string(err_type); + err_str = gb_string_make(c->tmp_allocator, + gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string)); + gb_string_free(type_string); + } + } else { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + err_str = gb_string_make(c->tmp_allocator, + gb_bprintf("mismatched types `%s` and `%s`", xt, yt)); + gb_string_free(yt); + gb_string_free(xt); + } + + if (err_str != NULL) { + error(ast_node_token(x->expr), "Cannot compare expression, %s", err_str); + x->type = t_untyped_bool; + } else { + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value)); + } else { + x->mode = Addressing_Value; + + update_expr_type(c, x->expr, default_type(x->type), true); + update_expr_type(c, y->expr, default_type(y->type), true); + } + + if (is_type_vector(base_type(y->type))) { + x->type = make_type_vector(c->allocator, t_bool, base_type(y->type)->Vector.count); + } else { + x->type = t_untyped_bool; + } + } + + if (err_str != NULL) { + gb_string_free(err_str); + }; + + gb_temp_arena_memory_end(tmp); +} + +void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + ast_node(be, BinaryExpr, node); + + ExactValue x_val = {0}; + if (x->mode == Addressing_Constant) { + x_val = exact_value_to_integer(x->value); + } + + bool x_is_untyped = is_type_untyped(x->type); + if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) { + gbString err_str = expr_to_string(x->expr); + error(ast_node_token(node), + "Shifted operand `%s` must be an integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_unsigned(y->type)) { + + } else if (is_type_untyped(y->type)) { + convert_to_typed(c, y, t_untyped_integer, 0); + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + return; + } + } else { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount `%s` must be an unsigned integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + + if (x->mode == Addressing_Constant) { + if (y->mode == Addressing_Constant) { + ExactValue y_val = exact_value_to_integer(y->value); + if (y_val.kind != ExactValue_Integer) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount `%s` must be an unsigned integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + u64 amount = cast(u64)y_val.value_integer; + if (amount > 1074) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount too large: `%s`", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + if (!is_type_integer(x->type)) { + // NOTE(bill): It could be an untyped float but still representable + // as an integer + x->type = t_untyped_integer; + } + + x->value = exact_value_shift(be->op, x_val, make_exact_value_integer(amount)); + + if (is_type_typed(x->type)) { + check_is_expressible(c, x, base_type(x->type)); + } + return; + } + + if (x_is_untyped) { + ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr)); + if (info != NULL) { + info->is_lhs = true; + } + x->mode = Addressing_Value; + return; + } + } + + if (y->mode == Addressing_Constant && y->value.value_integer < 0) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount cannot be negative: `%s`", err_str); + gb_string_free(err_str); + } + + x->mode = Addressing_Value; +} + +bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { + if (check_is_assignable_to(c, operand, y)) { + return true; + } + + Type *x = operand->type; + Type *xb = base_type(x); + Type *yb = base_type(y); + if (are_types_identical(xb, yb)) { + return true; + } + xb = get_enum_base_type(x); + yb = get_enum_base_type(y); + + + // Cast between booleans and integers + if (is_type_boolean(xb) || is_type_integer(xb)) { + 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)) { + return true; + } + } + + // Cast between pointers + if (is_type_pointer(xb) && is_type_pointer(yb)) { + return true; + } + + // (u)int <-> pointer + if (is_type_int_or_uint(xb) && is_type_rawptr(yb)) { + return true; + } + if (is_type_rawptr(xb) && is_type_int_or_uint(yb)) { + return true; + } + + // []byte/[]u8 <-> string + if (is_type_u8_slice(xb) && is_type_string(yb)) { + return true; + } + if (is_type_string(xb) && is_type_u8_slice(yb)) { + if (is_type_typed(xb)) { + return true; + } + } + + // proc <-> proc + if (is_type_proc(xb) && is_type_proc(yb)) { + return true; + } + + // proc -> rawptr + if (is_type_proc(xb) && is_type_rawptr(yb)) { + return true; + } + + return false; +} + +String check_down_cast_name(Type *dst_, Type *src_) { + String result = {0}; + Type *dst = type_deref(dst_); + Type *src = type_deref(src_); + Type *dst_s = base_type(dst); + GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s)); + for (isize i = 0; i < dst_s->Record.field_count; i++) { + Entity *f = dst_s->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + if (f->flags & EntityFlag_Anonymous) { + if (are_types_identical(f->type, src_)) { + return f->token.string; + } + if (are_types_identical(type_deref(f->type), src_)) { + return f->token.string; + } + + if (!is_type_pointer(f->type)) { + result = check_down_cast_name(f->type, src_); + if (result.len > 0) { + return result; + } + } + } + } + + return result; +} + +Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offset, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + ast_node(be, BinaryExpr, node); + GB_ASSERT(is_type_pointer(ptr->type)); + GB_ASSERT(is_type_integer(offset->type)); + GB_ASSERT(op == Token_Add || op == Token_Sub); + + Operand operand = {0}; + operand.mode = Addressing_Value; + operand.type = ptr->type; + operand.expr = node; + + if (base_type(ptr->type) == t_rawptr) { + gbString str = type_to_string(ptr->type); + error(ast_node_token(node), "Invalid pointer type for pointer arithmetic: `%s`", str); + gb_string_free(str); + operand.mode = Addressing_Invalid; + return operand; + } + + + if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { + i64 elem_size = type_size_of(c->sizes, c->allocator, ptr->type); + i64 ptr_val = ptr->value.value_pointer; + i64 offset_val = exact_value_to_integer(offset->value).value_integer; + i64 new_ptr_val = ptr_val; + if (op == Token_Add) { + new_ptr_val += elem_size*offset_val; + } else { + new_ptr_val -= elem_size*offset_val; + } + operand.mode = Addressing_Constant; + operand.value = make_exact_value_pointer(new_ptr_val); + } + + return operand; +} + +void check_binary_expr(Checker *c, Operand *x, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + Operand y_ = {0}, *y = &y_; + + ast_node(be, BinaryExpr, 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) { + return; + } + + bool is_const_expr = x->mode == Addressing_Constant; + bool can_convert = false; + + Type *bt = base_type(type); + if (is_const_expr && is_type_constant_type(bt)) { + if (bt->kind == Type_Basic) { + if (check_value_is_expressible(c, x->value, bt, &x->value)) { + can_convert = true; + } + } + } else if (check_is_castable_to(c, x, type)) { + if (x->mode != Addressing_Constant) { + x->mode = Addressing_Value; + } + can_convert = true; + } + + if (!can_convert) { + gbString expr_str = expr_to_string(x->expr); + gbString to_type = type_to_string(type); + gbString from_type = type_to_string(x->type); + error(ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); + gb_string_free(from_type); + gb_string_free(to_type); + gb_string_free(expr_str); + + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + Type *final_type = type; + if (is_const_expr && !is_type_constant_type(type)) { + final_type = default_type(x->type); + } + update_expr_type(c, x->expr, final_type, true); + } + + x->type = type; + return; + } 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) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(x->expr); + error(ast_node_token(x->expr), "Cannot transmute constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(x->expr); + error(ast_node_token(x->expr), "Cannot transmute untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + i64 srcz = type_size_of(c->sizes, c->allocator, x->type); + i64 dstz = type_size_of(c->sizes, c->allocator, type); + if (srcz != dstz) { + gbString expr_str = expr_to_string(x->expr); + gbString type_str = type_to_string(type); + error(ast_node_token(x->expr), "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, srcz, dstz); + gb_string_free(type_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + x->type = type; + + return; + } else if (be->op.kind == Token_down_cast) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `down_cast` a constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `down_cast` an untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(is_type_pointer(x->type) && is_type_pointer(type))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointers: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + Type *src = type_deref(x->type); + Type *dst = type_deref(type); + Type *bsrc = base_type(src); + Type *bdst = base_type(dst); + + if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(is_type_struct(bdst) || is_type_raw_union(bdst))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + String param_name = check_down_cast_name(dst, src); + if (param_name.len == 0) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Illegal `down_cast`: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + x->mode = Addressing_Value; + x->type = type; + return; + } else if (be->op.kind == Token_union_cast) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `union_cast` a constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `union_cast` an untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + bool src_is_ptr = is_type_pointer(x->type); + bool dst_is_ptr = is_type_pointer(type); + Type *src = type_deref(x->type); + Type *dst = type_deref(type); + Type *bsrc = base_type(src); + Type *bdst = base_type(dst); + + if (src_is_ptr != dst_is_ptr) { + gbString src_type_str = type_to_string(x->type); + gbString dst_type_str = type_to_string(type); + error(ast_node_token(node), "Invalid `union_cast` types: `%s` and `%s`", src_type_str, dst_type_str); + gb_string_free(dst_type_str); + gb_string_free(src_type_str); + x->mode = Addressing_Invalid; + return; + } + + if (!is_type_union(src)) { + error(ast_node_token(node), "`union_cast` can only operate on unions"); + x->mode = Addressing_Invalid; + return; + } + + bool ok = false; + for (isize i = 1; i < bsrc->Record.field_count; i++) { + Entity *f = bsrc->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + ok = true; + break; + } + } + + if (!ok) { + gbString expr_str = expr_to_string(node); + gbString dst_type_str = type_to_string(type); + error(ast_node_token(node), "Cannot `union_cast` `%s` to `%s`", expr_str, dst_type_str); + gb_string_free(dst_type_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); + Token tok = make_token_ident(str_lit("")); + variables[0] = make_entity_param(c->allocator, NULL, tok, type, false); + variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false); + + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = 2; + + x->type = tuple; + x->mode = Addressing_Value; + return; + } + + check_expr(c, x, be->left); + check_expr(c, y, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + x->expr = y->expr; + return; + } + + Token op = be->op; + + if (token_is_shift(op)) { + check_shift(c, x, y, node); + return; + } + + if (op.kind == Token_Add || op.kind == Token_Sub) { + if (is_type_pointer(x->type) && is_type_integer(y->type)) { + *x = check_ptr_addition(c, op.kind, x, y, node); + return; + } else if (is_type_integer(x->type) && is_type_pointer(y->type)) { + if (op.kind == Token_Sub) { + gbString lhs = expr_to_string(x->expr); + gbString rhs = expr_to_string(y->expr); + error(ast_node_token(node), "Invalid pointer arithmetic, did you mean `%s %.*s %s`?", rhs, LIT(op.string), lhs); + gb_string_free(rhs); + gb_string_free(lhs); + x->mode = Addressing_Invalid; + return; + } + *x = check_ptr_addition(c, op.kind, y, x, node); + return; + } + } + + + convert_to_typed(c, x, y->type, 0); + if (x->mode == Addressing_Invalid) { + return; + } + convert_to_typed(c, y, x->type, 0); + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + return; + } + + if (token_is_comparison(op)) { + check_comparison(c, x, y, op); + return; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + x->mode = Addressing_Invalid; + return; + } + + if (!check_binary_op(c, x, op)) { + x->mode = Addressing_Invalid; + return; + } + + switch (op.kind) { + case Token_Quo: + case Token_Mod: + case Token_QuoEq: + case Token_ModEq: + if ((x->mode == Addressing_Constant || is_type_integer(x->type)) && + y->mode == Addressing_Constant) { + bool fail = false; + switch (y->value.kind) { + case ExactValue_Integer: + if (y->value.value_integer == 0) { + fail = true; + } + break; + case ExactValue_Float: + if (y->value.value_float == 0.0) { + fail = true; + } + break; + } + + if (fail) { + error(ast_node_token(y->expr), "Division by zero not allowed"); + x->mode = Addressing_Invalid; + return; + } + } + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + Type *type = base_type(x->type); + if (is_type_pointer(type)) { + GB_ASSERT(op.kind == Token_Sub); + i64 bytes = a.value_pointer - b.value_pointer; + i64 diff = bytes/type_size_of(c->sizes, c->allocator, type); + x->value = make_exact_value_pointer(diff); + return; + } + + if (type->kind != Type_Basic) { + gbString xt = type_to_string(x->type); + gbString err_str = expr_to_string(node); + error(op, "Invalid type, `%s`, for constant binary expression `%s`", xt, err_str); + gb_string_free(err_str); + gb_string_free(xt); + x->mode = Addressing_Invalid; + return; + } + + if (op.kind == Token_Quo && is_type_integer(type)) { + op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers + } + x->value = exact_binary_operator_value(op, a, b); + if (is_type_typed(type)) { + if (node != NULL) { + x->expr = node; + } + check_is_expressible(c, x, type); + } + return; + } + + x->mode = Addressing_Value; +} + + +void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) { + HashKey key = hash_pointer(e); + ExprInfo *found = map_expr_info_get(&c->info.untyped, key); + if (found == NULL) { + return; + } + + switch (e->kind) { + case_ast_node(ue, UnaryExpr, e); + 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) { + break; + } + if (!token_is_comparison(be->op)) { + if (token_is_shift(be->op)) { + update_expr_type(c, be->left, type, final); + } else { + update_expr_type(c, be->left, type, final); + update_expr_type(c, be->right, type, final); + } + } + case_end; + } + + if (!final && is_type_untyped(type)) { + found->type = base_type(type); + map_expr_info_set(&c->info.untyped, key, *found); + } else { + ExprInfo old = *found; + map_expr_info_remove(&c->info.untyped, key); + + if (old.is_lhs && !is_type_integer(type)) { + gbString expr_str = expr_to_string(e); + gbString type_str = type_to_string(type); + error(ast_node_token(e), "Shifted operand %s must be an integer, got %s", expr_str, type_str); + gb_string_free(type_str); + gb_string_free(expr_str); + return; + } + + add_type_and_value(&c->info, e, found->mode, type, found->value); + } +} + +void update_expr_value(Checker *c, AstNode *e, ExactValue value) { + ExprInfo *found = map_expr_info_get(&c->info.untyped, hash_pointer(e)); + if (found) { + found->value = value; + } +} + +void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) { + gbString expr_str = expr_to_string(operand->expr); + gbString type_str = type_to_string(target_type); + char *extra_text = ""; + + if (operand->mode == Addressing_Constant) { + if (operand->value.value_integer == 0) { + if (str_ne(make_string_c(expr_str), str_lit("nil"))) { // HACK NOTE(bill): Just in case + // NOTE(bill): Doesn't matter what the type is as it's still zero in the union + extra_text = " - Did you want `nil`?"; + } + } + } + error(ast_node_token(operand->expr), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text); + + gb_string_free(type_str); + gb_string_free(expr_str); + operand->mode = Addressing_Invalid; +} + +// NOTE(bill): Set initial level to 0 +void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level) { + GB_ASSERT_NOT_NULL(target_type); + if (operand->mode == Addressing_Invalid || + is_type_typed(operand->type) || + target_type == t_invalid) { + return; + } + + if (is_type_untyped(target_type)) { + Type *x = operand->type; + Type *y = target_type; + if (is_type_numeric(x) && is_type_numeric(y)) { + if (x < y) { + operand->type = target_type; + update_expr_type(c, operand->expr, target_type, false); + } + } else if (x != y) { + convert_untyped_error(c, operand, target_type); + } + return; + } + + Type *t = get_enum_base_type(base_type(target_type)); + switch (t->kind) { + case Type_Basic: + if (operand->mode == Addressing_Constant) { + check_is_expressible(c, operand, t); + if (operand->mode == Addressing_Invalid) { + return; + } + update_expr_value(c, operand->expr, operand->value); + } else { + switch (operand->type->Basic.kind) { + case Basic_UntypedBool: + if (!is_type_boolean(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + case Basic_UntypedInteger: + case Basic_UntypedFloat: + case Basic_UntypedRune: + if (!is_type_numeric(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + + case Basic_UntypedNil: + if (!type_has_nil(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + } + } + break; + + case Type_Maybe: + if (is_type_untyped_nil(operand->type)) { + // Okay + } else if (level == 0) { + convert_to_typed(c, operand, t->Maybe.elem, level+1); + return; + } + + default: + if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + } + + + + operand->type = target_type; +} + +bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) { + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, index_value); + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; + return false; + } + + convert_to_typed(c, &operand, t_int, 0); + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; + return false; + } + + if (!is_type_integer(get_enum_base_type(operand.type))) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` must be an integer", expr_str); + gb_string_free(expr_str); + if (value) *value = 0; + return false; + } + + if (operand.mode == Addressing_Constant && + (c->context.stmt_state_flags & StmtStateFlag_bounds_check) != 0) { + i64 i = exact_value_to_integer(operand.value).value_integer; + if (i < 0) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` cannot be a negative value", expr_str); + gb_string_free(expr_str); + if (value) *value = 0; + return false; + } + + if (max_count >= 0) { // NOTE(bill): Do array bound checking + if (value) *value = i; + if (i >= max_count) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count); + gb_string_free(expr_str); + return false; + } + + return true; + } + } + + // NOTE(bill): It's alright :D + if (value) *value = -1; + return true; +} + +Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { + ast_node(se, SelectorExpr, node); + + bool check_op_expr = true; + Entity *expr_entity = NULL; + Entity *entity = NULL; + Selection sel = {0}; // NOTE(bill): Not used if it's an import name + + AstNode *op_expr = se->expr; + AstNode *selector = unparen_expr(se->selector); + if (selector == NULL) { + goto error; + } + + 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; + entity = scope_lookup_entity(e->ImportName.scope, sel_name); + if (entity == NULL) { + error(ast_node_token(op_expr), "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name)); + goto error; + } + if (entity->type == NULL) { // Not setup yet + check_entity_decl(c, entity, NULL, NULL, NULL); + } + GB_ASSERT(entity->type != NULL); + // bool is_not_exported = !is_entity_exported(entity); + + b32 is_not_exported = true; + + Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name)); + if (!found) { + is_not_exported = false; + } else { + Entity *f = *found; + if (f->kind == Entity_ImportName) { + is_not_exported = true; + } + } + + // // TODO(bill): Fix this for `#import "file.odin" as .` + // if (true || is_not_exported) { + // Entity **found = + // if (!found && e->ImportName.scope != entity->scope) { + // is_not_exported = false; + // } + // gb_printf("%.*s\n", LIT(entity->token.string)); + // } + + if (is_not_exported) { + gbString sel_str = expr_to_string(selector); + error(ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name)); + gb_string_free(sel_str); + // NOTE(bill): Not really an error so don't goto error + } + + add_entity_use(c, selector, entity); + } + } + if (check_op_expr) { + check_expr_base(c, operand, op_expr, NULL); + if (operand->mode == Addressing_Invalid) { + goto error; + } + } + + + if (entity == NULL) { + sel = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type); + entity = sel.entity; + } + if (entity == NULL) { + gbString op_str = expr_to_string(op_expr); + gbString type_str = type_to_string(operand->type); + gbString sel_str = expr_to_string(selector); + error(ast_node_token(op_expr), "`%s` (`%s`) has no field `%s`", op_str, type_str, sel_str); + gb_string_free(sel_str); + gb_string_free(type_str); + gb_string_free(op_str); + 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); + error(ast_node_token(op_expr), "Cannot access non-constant field `%s` from `%s`", sel_str, op_str); + gb_string_free(sel_str); + gb_string_free(type_str); + gb_string_free(op_str); + goto error; + } + + + add_entity_use(c, selector, entity); + + switch (entity->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->value = entity->Constant.value; + break; + case Entity_Variable: + // TODO(bill): This is the rule I need? + if (sel.indirect || operand->mode != Addressing_Value) { + operand->mode = Addressing_Variable; + } + break; + case Entity_TypeName: + operand->mode = Addressing_Type; + break; + case Entity_Procedure: + operand->mode = Addressing_Value; + break; + case Entity_Builtin: + operand->mode = Addressing_Builtin; + operand->builtin_id = entity->Builtin.id; + break; + + // NOTE(bill): These cases should never be hit but are here for sanity reasons + case Entity_Nil: + operand->mode = Addressing_Value; + break; + case Entity_ImplicitValue: + operand->mode = Addressing_Value; + break; + } + + operand->type = entity->type; + operand->expr = node; + + return entity; + +error: + operand->mode = Addressing_Invalid; + operand->expr = node; + return NULL; +} + +bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { + GB_ASSERT(call->kind == AstNode_CallExpr); + ast_node(ce, CallExpr, call); + BuiltinProc *bp = &builtin_procs[id]; + { + char *err = NULL; + if (ce->args.count < bp->arg_count) { + err = "Too few"; + } else if (ce->args.count > bp->arg_count && !bp->variadic) { + err = "Too many"; + } + + if (err) { + ast_node(proc, Ident, ce->proc); + error(ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", + err, LIT(proc->string), + bp->arg_count, ce->args.count); + return false; + } + } + + switch (id) { + case BuiltinProc_new: + case BuiltinProc_new_slice: + case BuiltinProc_size_of: + case BuiltinProc_align_of: + case BuiltinProc_offset_of: + case BuiltinProc_type_info: + // NOTE(bill): The first arg may be a Type, this will be checked case by case + break; + default: + check_multi_expr(c, operand, ce->args.e[0]); + } + + switch (id) { + case BuiltinProc_new: { + // new :: proc(Type) -> ^Type + Operand op = {0}; + check_expr_or_type(c, &op, ce->args.e[0]); + Type *type = op.type; + if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `new`"); + return false; + } + operand->mode = Addressing_Value; + operand->type = make_type_pointer(c->allocator, type); + } break; + case BuiltinProc_new_slice: { + // new_slice :: proc(Type, len: int[, cap: int]) -> []Type + Operand op = {0}; + check_expr_or_type(c, &op, ce->args.e[0]); + Type *type = op.type; + if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `new_slice`"); + return false; + } + + AstNode *len = ce->args.e[1]; + AstNode *cap = NULL; + if (ce->args.count > 2) { + cap = ce->args.e[2]; + } + + check_expr(c, &op, len); + if (op.mode == Addressing_Invalid) { + return false; + } + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Length for `new_slice` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (cap != NULL) { + check_expr(c, &op, cap); + if (op.mode == Addressing_Invalid) { + return false; + } + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Capacity for `new_slice` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + if (ce->args.count > 3) { + error(ast_node_token(call), + "Too many arguments to `new_slice`, expected either 2 or 3"); + return false; + } + } + + operand->mode = Addressing_Value; + operand->type = make_type_slice(c->allocator, type); + } break; + + case BuiltinProc_size_of: { + // size_of :: proc(Type) -> untyped int + Type *type = check_type(c, ce->args.e[0]); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `size_of`"); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, type)); + operand->type = t_untyped_integer; + + } break; + + case BuiltinProc_size_of_val: + // size_of_val :: proc(val: Type) -> untyped int + check_assignment(c, operand, NULL, str_lit("argument of `size_of_val`")); + if (operand->mode == Addressing_Invalid) { + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, operand->type)); + operand->type = t_untyped_integer; + break; + + case BuiltinProc_align_of: { + // align_of :: proc(Type) -> untyped int + Type *type = check_type(c, ce->args.e[0]); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `align_of`"); + return false; + } + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, type)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_align_of_val: + // align_of_val :: proc(val: Type) -> untyped int + check_assignment(c, operand, NULL, str_lit("argument of `align_of_val`")); + if (operand->mode == Addressing_Invalid) { + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, operand->type)); + operand->type = t_untyped_integer; + break; + + case BuiltinProc_offset_of: { + // offset_of :: proc(Type, field) -> untyped int + Operand op = {0}; + Type *bt = check_type(c, ce->args.e[0]); + Type *type = base_type(bt); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `offset_of`"); + return false; + } + + AstNode *field_arg = unparen_expr(ce->args.e[1]); + if (field_arg == NULL || + field_arg->kind != AstNode_Ident) { + error(ast_node_token(field_arg), "Expected an identifier for field argument"); + return false; + } + if (is_type_array(type) || is_type_vector(type)) { + error(ast_node_token(field_arg), "Invalid type for `offset_of`"); + return false; + } + + + ast_node(arg, Ident, field_arg); + Selection sel = lookup_field(c->allocator, type, arg->string, operand->mode == Addressing_Type); + if (sel.entity == NULL) { + gbString type_str = type_to_string(bt); + error(ast_node_token(ce->args.e[0]), + "`%s` has no field named `%.*s`", type_str, LIT(arg->string)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ast_node_token(ce->args.e[0]), + "Field `%.*s` is embedded via a pointer in `%s`", LIT(arg->string), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_offset_of_val: { + // offset_of_val :: proc(val: expression) -> untyped int + AstNode *arg = unparen_expr(ce->args.e[0]); + if (arg->kind != AstNode_SelectorExpr) { + gbString str = expr_to_string(arg); + error(ast_node_token(arg), "`%s` is not a selector expression", str); + return false; + } + ast_node(s, SelectorExpr, arg); + + check_expr(c, operand, s->expr); + if (operand->mode == Addressing_Invalid) { + return false; + } + + Type *type = operand->type; + if (base_type(type)->kind == Type_Pointer) { + Type *p = base_type(type); + if (is_type_struct(p)) { + type = p->Pointer.elem; + } + } + if (is_type_array(type) || is_type_vector(type)) { + error(ast_node_token(arg), "Invalid type for `offset_of_val`"); + return false; + } + + ast_node(i, Ident, s->selector); + Selection sel = lookup_field(c->allocator, type, i->string, operand->mode == Addressing_Type); + if (sel.entity == NULL) { + gbString type_str = type_to_string(type); + error(ast_node_token(arg), + "`%s` has no field named `%.*s`", type_str, LIT(i->string)); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(type); + error(ast_node_token(ce->args.e[0]), + "Field `%.*s` is embedded via a pointer in `%s`", LIT(i->string), type_str); + gb_string_free(type_str); + return false; + } + + + operand->mode = Addressing_Constant; + // IMPORTANT TODO(bill): Fix for anonymous fields + operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_type_of_val: + // type_of_val :: proc(val: Type) -> type(Type) + check_assignment(c, operand, NULL, str_lit("argument of `type_of_val`")); + if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) { + return false; + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_info: { + // type_info :: proc(Type) -> ^Type_Info + AstNode *expr = ce->args.e[0]; + Type *type = check_type(c, expr); + if (type == NULL || type == t_invalid) { + error(ast_node_token(expr), "Invalid argument to `type_info`"); + return false; + } + + add_type_info_type(c, type); + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + } break; + + case BuiltinProc_type_info_of_val: { + // type_info_of_val :: proc(val: Type) -> ^Type_Info + AstNode *expr = ce->args.e[0]; + + check_assignment(c, operand, NULL, str_lit("argument of `type_info_of_val`")); + if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) + return false; + add_type_info_type(c, operand->type); + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + } break; + + + + case BuiltinProc_compile_assert: + // compile_assert :: proc(cond: bool) + + if (!is_type_boolean(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a constant boolean", str); + gb_string_free(str); + return false; + } + if (!operand->value.value_bool) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "Compile time assertion: `%s`", str); + gb_string_free(str); + } + break; + + case BuiltinProc_assert: + // assert :: proc(cond: bool) + + if (!is_type_boolean(operand->type)) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a boolean", str); + gb_string_free(str); + return false; + } + + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_panic: + // panic :: proc(msg: string) + + if (!is_type_string(operand->type)) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a string", str); + gb_string_free(str); + return false; + } + + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_copy: { + // copy :: proc(x, y: []Type) -> int + Type *dest_type = NULL, *src_type = NULL; + + Type *d = base_type(operand->type); + if (d->kind == Type_Slice) { + dest_type = d->Slice.elem; + } + Operand op = {0}; + check_expr(c, &op, ce->args.e[1]); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *s = base_type(op.type); + if (s->kind == Type_Slice) { + src_type = s->Slice.elem; + } + + if (dest_type == NULL || src_type == NULL) { + error(ast_node_token(call), "`copy` only expects slices as arguments"); + return false; + } + + if (!are_types_identical(dest_type, src_type)) { + gbString d_arg = expr_to_string(ce->args.e[0]); + gbString s_arg = expr_to_string(ce->args.e[1]); + gbString d_str = type_to_string(dest_type); + gbString s_str = type_to_string(src_type); + error(ast_node_token(call), + "Arguments to `copy`, %s, %s, have different elem types: %s vs %s", + d_arg, s_arg, d_str, s_str); + gb_string_free(s_str); + gb_string_free(d_str); + gb_string_free(s_arg); + gb_string_free(d_arg); + return false; + } + + operand->type = t_int; // Returns number of elems copied + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_append: { + // append :: proc(x : ^[]Type, y : Type) -> bool + Type *x_type = NULL, *y_type = NULL; + x_type = base_type(operand->type); + + Operand op = {0}; + check_expr(c, &op, ce->args.e[1]); + if (op.mode == Addressing_Invalid) { + return false; + } + y_type = base_type(op.type); + + if (!(is_type_pointer(x_type) && is_type_slice(x_type->Pointer.elem))) { + error(ast_node_token(call), "First argument to `append` must be a pointer to a slice"); + return false; + } + + Type *elem_type = x_type->Pointer.elem->Slice.elem; + if (!check_is_assignable_to(c, &op, elem_type)) { + gbString d_arg = expr_to_string(ce->args.e[0]); + gbString s_arg = expr_to_string(ce->args.e[1]); + gbString d_str = type_to_string(elem_type); + gbString s_str = type_to_string(y_type); + error(ast_node_token(call), + "Arguments to `append`, %s, %s, have different element types: %s vs %s", + d_arg, s_arg, d_str, s_str); + gb_string_free(s_str); + gb_string_free(d_str); + gb_string_free(s_arg); + gb_string_free(d_arg); + return false; + } + + operand->type = t_bool; // Returns if it was successful + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_swizzle: { + // swizzle :: proc(v: {N}T, T...) -> {M}T + Type *vector_type = base_type(operand->type); + if (!is_type_vector(vector_type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "You can only `swizzle` a vector, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + isize max_count = vector_type->Vector.count; + isize arg_count = 0; + for_array(i, ce->args) { + if (i == 0) { + continue; + } + AstNode *arg = ce->args.e[i]; + Operand op = {0}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) { + error(ast_node_token(op.expr), "Indices to `swizzle` must be constant integers"); + return false; + } + + if (op.value.value_integer < 0) { + error(ast_node_token(op.expr), "Negative `swizzle` index"); + return false; + } + + if (max_count <= op.value.value_integer) { + error(ast_node_token(op.expr), "`swizzle` index exceeds vector length"); + return false; + } + + arg_count++; + } + + if (arg_count > max_count) { + error(ast_node_token(call), "Too many `swizzle` indices, %td > %td", arg_count, max_count); + return false; + } + + Type *elem_type = vector_type->Vector.elem; + operand->type = make_type_vector(c->allocator, elem_type, arg_count); + operand->mode = Addressing_Value; + } break; + +#if 0 + case BuiltinProc_ptr_offset: { + // ptr_offset :: proc(ptr: ^T, offset: int) -> ^T + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_offset`, got `%s`", + type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + AstNode *offset = ce->args.e[1]; + Operand op = {0}; + check_expr(c, &op, offset); + if (op.mode == Addressing_Invalid) + return false; + Type *offset_type = base_type(op.type); + if (!is_type_integer(offset_type)) { + error(ast_node_token(op.expr), "Pointer offsets for `ptr_offset` must be an integer"); + return false; + } + + if (operand->mode == Addressing_Constant && + op.mode == Addressing_Constant) { + i64 ptr = operand->value.value_pointer; + i64 elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem); + ptr += elem_size * op.value.value_integer; + operand->value.value_pointer = ptr; + } else { + operand->mode = Addressing_Value; + } + + } break; + + case BuiltinProc_ptr_sub: { + // ptr_sub :: proc(a, b: ^T) -> int + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_add`, got `%s`", + type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + AstNode *offset = ce->args[1]; + Operand op = {0}; + check_expr(c, &op, offset); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_pointer(op.type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_add`, got `%s`", + type_str); + return false; + } + + if (base_type(op.type) == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + if (!are_types_identical(operand->type, op.type)) { + gbString a = type_to_string(operand->type); + gbString b = type_to_string(op.type); + defer (gb_string_free(a)); + defer (gb_string_free(b)); + error(ast_node_token(op.expr), + "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b); + return false; + } + + operand->type = t_int; + + if (operand->mode == Addressing_Constant && + op.mode == Addressing_Constant) { + u8 *ptr_a = cast(u8 *)operand->value.value_pointer; + u8 *ptr_b = cast(u8 *)op.value.value_pointer; + isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem); + operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size); + } else { + operand->mode = Addressing_Value; + } + } break; +#endif + + case BuiltinProc_slice_ptr: { + // slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a pointer to `slice_ptr`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + AstNode *len = ce->args.e[1]; + AstNode *cap = NULL; + if (ce->args.count > 2) { + cap = ce->args.e[2]; + } + + Operand op = {0}; + check_expr(c, &op, len); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Length for `slice_ptr` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (cap != NULL) { + check_expr(c, &op, cap); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Capacity for `slice_ptr` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + if (ce->args.count > 3) { + error(ast_node_token(call), + "Too many arguments to `slice_ptr`, expected either 2 or 3"); + return false; + } + } + + operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem); + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_min: { + // min :: proc(a, b: comparable) -> comparable + Type *type = base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + AstNode *other_arg = ce->args.e[1]; + Operand a = *operand; + Operand b = {0}; + check_expr(c, &b, other_arg); + 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); + error(ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token lt = {Token_Lt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(lt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + convert_to_typed(c, &a, b.type, 0); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type, 0); + 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); + error(ast_node_token(call), + "Mismatched types to `min`, `%s` vs `%s`", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + } break; + + case BuiltinProc_max: { + // min :: proc(a, b: comparable) -> comparable + Type *type = base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + AstNode *other_arg = ce->args.e[1]; + Operand a = *operand; + Operand b = {0}; + check_expr(c, &b, other_arg); + 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); + error(ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token gt = {Token_Gt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(gt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + convert_to_typed(c, &a, b.type, 0); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type, 0); + 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); + error(ast_node_token(call), + "Mismatched types to `max`, `%s` vs `%s`", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + } break; + + case BuiltinProc_abs: { + // abs :: proc(n: numeric) -> numeric + Type *type = base_type(operand->type); + if (!is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a numeric type to `abs`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + switch (operand->value.kind) { + case ExactValue_Integer: + operand->value.value_integer = gb_abs(operand->value.value_integer); + break; + case ExactValue_Float: + operand->value.value_float = gb_abs(operand->value.value_float); + break; + default: + GB_PANIC("Invalid numeric constant"); + break; + } + } else { + operand->mode = Addressing_Value; + } + + operand->type = type; + } break; + + case BuiltinProc_enum_to_string: { + Type *type = base_type(operand->type); + if (!is_type_enum(type)) { + gbString type_str = type_to_string(operand->type); + gb_string_free(type_str); + error(ast_node_token(call), + "Expected an enum to `enum_to_string`, got `%s`", + type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + ExactValue value = make_exact_value_string(str_lit("")); + if (operand->value.kind == ExactValue_Integer) { + i64 index = operand->value.value_integer; + for (isize i = 0; i < type->Record.other_field_count; i++) { + Entity *f = type->Record.other_fields[i]; + if (f->kind == Entity_Constant && f->Constant.value.kind == ExactValue_Integer) { + i64 fv = f->Constant.value.value_integer; + if (index == fv) { + value = make_exact_value_string(f->token.string); + break; + } + } + } + } + + operand->value = value; + operand->type = t_string; + return true; + } + + add_type_info_type(c, operand->type); + + operand->mode = Addressing_Value; + operand->type = t_string; + } break; + } + + return true; +} + + +void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { + GB_ASSERT(call->kind == AstNode_CallExpr); + GB_ASSERT(proc_type->kind == Type_Proc); + ast_node(ce, CallExpr, call); + + isize param_count = 0; + bool variadic = proc_type->Proc.variadic; + bool vari_expand = (ce->ellipsis.pos.line != 0); + + if (proc_type->Proc.params != NULL) { + param_count = proc_type->Proc.params->Tuple.variable_count; + if (variadic) { + param_count--; + } + } + + if (vari_expand && !variadic) { + error(ce->ellipsis, + "Cannot use `..` in call to a non-variadic procedure: `%.*s`", + LIT(ce->proc->Ident.string)); + return; + } + + if (ce->args.count == 0 && param_count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2*param_count); + + for_array(i, ce->args) { + Operand o = {0}; + check_multi_expr(c, &o, ce->args.e[i]); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + if (variadic && i >= param_count) { + error(ast_node_token(ce->args.e[i]), + "`..` in a variadic procedure cannot be applied to a %td-valued expression", tuple->variable_count); + operand->mode = Addressing_Invalid; + goto end; + } + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + i32 error_code = 0; + if (operands.count < param_count) { + error_code = -1; + } else if (!variadic && operands.count > param_count) { + error_code = +1; + } + if (error_code != 0) { + char *err_fmt = "Too many arguments for `%s`, expected %td arguments"; + if (error_code < 0) { + err_fmt = "Too few arguments for `%s`, expected %td arguments"; + } + + gbString proc_str = expr_to_string(ce->proc); + error(ast_node_token(call), err_fmt, proc_str, param_count); + gb_string_free(proc_str); + operand->mode = Addressing_Invalid; + goto end; + } + + GB_ASSERT(proc_type->Proc.params != NULL); + Entity **sig_params = proc_type->Proc.params->Tuple.variables; + isize operand_index = 0; + for (; operand_index < param_count; operand_index++) { + Type *arg_type = sig_params[operand_index]->type; + Operand o = operands.e[operand_index]; + if (variadic) { + o = operands.e[operand_index]; + } + check_assignment(c, &o, arg_type, str_lit("argument")); + } + + if (variadic) { + bool variadic_expand = false; + Type *slice = sig_params[param_count]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; + for (; operand_index < operands.count; operand_index++) { + Operand o = operands.e[operand_index]; + if (vari_expand) { + variadic_expand = true; + t = slice; + if (operand_index != param_count) { + error(ast_node_token(o.expr), + "`..` in a variadic procedure can only have one variadic argument at the end"); + break; + } + } + check_assignment(c, &o, t, str_lit("argument")); + } + } +end: + gb_temp_arena_memory_end(tmp); +} + + +Entity *find_using_index_expr(Type *t) { + t = base_type(t); + if (t->kind != Type_Record) { + return NULL; + } + + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + if (f->kind == Entity_Variable && + f->flags & (EntityFlag_Anonymous|EntityFlag_Field)) { + if (is_type_indexable(f->type)) { + return f; + } + Entity *res = find_using_index_expr(f->type); + if (res != NULL) { + return res; + } + } + } + return NULL; +} + +ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { + GB_ASSERT(call->kind == AstNode_CallExpr); + ast_node(ce, CallExpr, call); + check_expr_or_type(c, operand, ce->proc); + + if (operand->mode == Addressing_Invalid) { + for_array(i, ce->args) { + check_expr_base(c, operand, ce->args.e[i], NULL); + } + operand->mode = Addressing_Invalid; + operand->expr = call; + return Expr_Stmt; + } + + + if (operand->mode == Addressing_Builtin) { + i32 id = operand->builtin_id; + if (!check_builtin_procedure(c, operand, call, id)) { + operand->mode = Addressing_Invalid; + } + operand->expr = call; + return builtin_procs[id].kind; + } + + Type *proc_type = base_type(operand->type); + if (proc_type == NULL || proc_type->kind != Type_Proc) { + AstNode *e = operand->expr; + gbString str = expr_to_string(e); + error(ast_node_token(e), "Cannot call a non-procedure: `%s`", str); + gb_string_free(str); + + operand->mode = Addressing_Invalid; + operand->expr = call; + + return Expr_Stmt; + } + + check_call_arguments(c, operand, proc_type, call); + + switch (proc_type->Proc.result_count) { + case 0: + operand->mode = Addressing_NoValue; + break; + case 1: + operand->mode = Addressing_Value; + operand->type = proc_type->Proc.results->Tuple.variables[0]->type; + break; + default: + operand->mode = Addressing_Value; + operand->type = proc_type->Proc.results; + break; + } + + operand->expr = call; + return Expr_Stmt; +} + +void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) { + check_expr_base(c, o, e, t); + check_not_tuple(c, o); + char *err_str = NULL; + switch (o->mode) { + case Addressing_NoValue: + err_str = "used as a value"; + break; + case Addressing_Type: + err_str = "is not an expression"; + break; + case Addressing_Builtin: + err_str = "must be called"; + break; + } + if (err_str != NULL) { + gbString str = expr_to_string(e); + error(ast_node_token(e), "`%s` %s", str, err_str); + gb_string_free(str); + o->mode = Addressing_Invalid; + } +} + +bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { + t = base_type(type_deref(t)); + + switch (t->kind) { + case Type_Basic: + if (is_type_string(t)) { + if (o->mode == Addressing_Constant) { + *max_count = o->value.value_string.len; + } + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t_u8; + return true; + } + break; + + case Type_Array: + *max_count = t->Array.count; + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t->Array.elem; + return true; + + case Type_Vector: + *max_count = t->Vector.count; + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t->Vector.elem; + return true; + + + case Type_Slice: + o->type = t->Slice.elem; + o->mode = Addressing_Variable; + return true; + } + + return false; +} + +ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { + ExprKind kind = Expr_Stmt; + + o->mode = Addressing_Invalid; + o->type = t_invalid; + + switch (node->kind) { + default: + goto error; + break; + + case_ast_node(be, BadExpr, node) + goto error; + case_end; + + case_ast_node(i, Ident, node); + check_identifier(c, o, node, type_hint, NULL); + case_end; + + case_ast_node(bl, BasicLit, node); + Type *t = t_invalid; + switch (bl->kind) { + case Token_Integer: t = t_untyped_integer; break; + case Token_Float: t = t_untyped_float; break; + case Token_String: t = t_untyped_string; break; + case Token_Rune: t = t_untyped_rune; break; + default: GB_PANIC("Unknown literal"); break; + } + o->mode = Addressing_Constant; + o->type = t; + o->value = make_exact_value_from_basic_literal(*bl); + case_end; + + case_ast_node(pl, ProcLit, node); + check_open_scope(c, pl->type); + c->context.decl = make_declaration_info(c->allocator, c->context.scope); + Type *proc_type = check_type(c, pl->type); + if (proc_type != NULL) { + check_proc_body(c, empty_token, c->context.decl, proc_type, pl->body); + o->mode = Addressing_Value; + o->type = proc_type; + check_close_scope(c); + } else { + gbString str = expr_to_string(node); + error(ast_node_token(node), "Invalid procedure literal `%s`", str); + gb_string_free(str); + check_close_scope(c); + goto error; + } + case_end; + + case_ast_node(cl, CompoundLit, node); + Type *type = type_hint; + bool ellipsis_array = false; + bool is_constant = true; + if (cl->type != NULL) { + type = NULL; + + // [..]Type + if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != NULL) { + if (cl->type->ArrayType.count->kind == AstNode_Ellipsis) { + type = make_type_array(c->allocator, check_type(c, cl->type->ArrayType.elem), -1); + ellipsis_array = true; + } + } + + if (type == NULL) { + type = check_type(c, cl->type); + } + } + + if (type == NULL) { + error(ast_node_token(node), "Missing type in compound literal"); + goto error; + } + + Type *t = base_type(type); + switch (t->kind) { + case Type_Record: { + if (!is_type_struct(t)) { + if (cl->elems.count != 0) { + error(ast_node_token(node), "Illegal compound literal"); + } + break; + } + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + { // Checker values + isize field_count = t->Record.field_count; + if (cl->elems.e[0]->kind == AstNode_FieldValue) { + bool *fields_visited = gb_alloc_array(c->allocator, bool, field_count); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (elem->kind != AstNode_FieldValue) { + error(ast_node_token(elem), + "Mixture of `field = value` and value elements in a structure literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != AstNode_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(ast_node_token(elem), + "Invalid field name `%s` in structure literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.string; + + Selection sel = lookup_field(c->allocator, type, name, o->mode == Addressing_Type); + if (sel.entity == NULL) { + error(ast_node_token(elem), + "Unknown field `%.*s` in structure literal", LIT(name)); + continue; + } + + if (sel.index.count > 1) { + error(ast_node_token(elem), + "Cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name)); + continue; + } + + Entity *field = t->Record.fields[sel.index.e[0]]; + add_entity_use(c, fv->field, field); + + if (fields_visited[sel.index.e[0]]) { + error(ast_node_token(elem), + "Duplicate field `%.*s` in structure literal", LIT(name)); + continue; + } + + fields_visited[sel.index.e[0]] = true; + check_expr(c, o, fv->value); + + if (base_type(field->type) == t_any) { + is_constant = false; + } + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + + check_assignment(c, o, field->type, str_lit("structure literal")); + } + } else { + for_array(index, cl->elems) { + AstNode *elem = cl->elems.e[index]; + if (elem->kind == AstNode_FieldValue) { + error(ast_node_token(elem), + "Mixture of `field = value` and value elements in a structure literal is not allowed"); + continue; + } + Entity *field = t->Record.fields_in_src_order[index]; + + check_expr(c, o, elem); + if (index >= field_count) { + error(ast_node_token(o->expr), "Too many values in structure literal, expected %td", field_count); + break; + } + + if (base_type(field->type) == t_any) { + is_constant = false; + } + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + check_assignment(c, o, field->type, str_lit("structure literal")); + } + if (cl->elems.count < field_count) { + error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } + + } break; + + case Type_Slice: + case Type_Array: + case Type_Vector: + { + Type *elem_type = NULL; + String context_name = {0}; + if (t->kind == Type_Slice) { + elem_type = t->Slice.elem; + context_name = str_lit("slice literal"); + } else if (t->kind == Type_Vector) { + elem_type = t->Vector.elem; + context_name = str_lit("vector literal"); + } else { + elem_type = t->Array.elem; + context_name = str_lit("array literal"); + } + + + i64 max = 0; + isize index = 0; + isize elem_count = cl->elems.count; + + if (base_type(elem_type) == t_any) { + is_constant = false; + } + + for (; index < elem_count; index++) { + AstNode *e = cl->elems.e[index]; + if (e->kind == AstNode_FieldValue) { + error(ast_node_token(e), + "`field = value` is only allowed in struct literals"); + continue; + } + + if (t->kind == Type_Array && + t->Array.count >= 0 && + index >= t->Array.count) { + error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for array literal", index, t->Array.count); + } + if (t->kind == Type_Vector && + t->Vector.count >= 0 && + index >= t->Vector.count) { + error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for vector literal", index, t->Vector.count); + } + + Operand operand = {0}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + if (is_constant) { + is_constant = operand.mode == Addressing_Constant; + } + } + if (max < index) { + max = index; + } + + if (t->kind == Type_Vector) { + if (t->Vector.count > 1 && gb_is_between(index, 2, t->Vector.count-1)) { + error(ast_node_token(cl->elems.e[0]), + "Expected either 1 (broadcast) or %td elements in vector literal, got %td", t->Vector.count, index); + } + } + + if (t->kind == Type_Array && ellipsis_array) { + t->Array.count = max; + } + } break; + + default: { + gbString str = type_to_string(type); + error(ast_node_token(node), "Invalid compound literal type `%s`", str); + gb_string_free(str); + goto error; + } break; + } + + if (is_constant) { + o->mode = Addressing_Constant; + o->value = make_exact_value_compound(node); + } else { + o->mode = Addressing_Value; + } + o->type = type; + case_end; + + case_ast_node(pe, ParenExpr, node); + kind = check_expr_base(c, o, pe->expr, type_hint); + o->expr = node; + case_end; + + + case_ast_node(te, TagExpr, node); + // TODO(bill): Tag expressions + error(ast_node_token(node), "Tag expressions are not supported yet"); + kind = check_expr_base(c, o, te->expr, type_hint); + o->expr = node; + case_end; + + case_ast_node(re, RunExpr, node); + // TODO(bill): Tag expressions + kind = check_expr_base(c, o, re->expr, type_hint); + o->expr = node; + case_end; + + + case_ast_node(ue, UnaryExpr, node); + check_expr(c, o, ue->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + check_unary_expr(c, o, ue->op, node); + if (o->mode == Addressing_Invalid) { + goto error; + } + case_end; + + + case_ast_node(be, BinaryExpr, node); + check_binary_expr(c, o, node); + if (o->mode == Addressing_Invalid) { + goto error; + } + case_end; + + + + case_ast_node(se, SelectorExpr, node); + check_selector(c, o, node); + case_end; + + + case_ast_node(ie, IndexExpr, node); + check_expr(c, o, ie->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + + Type *t = base_type(type_deref(o->type)); + bool is_const = o->mode == Addressing_Constant; + + i64 max_count = -1; + bool valid = check_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) { + valid = check_set_index_data(o, found->type, &max_count); + } + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + 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; + } + + if (ie->index == NULL) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Missing index for `%s`", str); + gb_string_free(str); + goto error; + } + + i64 index = 0; + bool ok = check_index_value(c, ie->index, max_count, &index); + + case_end; + + + + case_ast_node(se, SliceExpr, node); + check_expr(c, o, se->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + + bool valid = false; + i64 max_count = -1; + Type *t = base_type(type_deref(o->type)); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t)) { + valid = true; + if (o->mode == Addressing_Constant) { + max_count = o->value.value_string.len; + } + if (se->max != NULL) { + error(ast_node_token(se->max), "Max (3rd) index not needed in substring expression"); + } + o->type = t_string; + } + break; + + case Type_Array: + valid = true; + max_count = t->Array.count; + if (o->mode != Addressing_Variable) { + gbString str = expr_to_string(node); + error(ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str); + gb_string_free(str); + goto error; + } + o->type = make_type_slice(c->allocator, t->Array.elem); + break; + + case Type_Slice: + valid = true; + break; + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot slice `%s`", str); + gb_string_free(str); + goto error; + } + + o->mode = Addressing_Value; + + i64 indices[3] = {0}; + AstNode *nodes[3] = {se->low, se->high, se->max}; + for (isize i = 0; i < gb_count_of(nodes); i++) { + i64 index = max_count; + if (nodes[i] != NULL) { + i64 capacity = -1; + if (max_count >= 0) + capacity = max_count; + i64 j = 0; + if (check_index_value(c, nodes[i], capacity, &j)) { + index = j; + } + } else if (i == 0) { + index = 0; + } + indices[i] = index; + } + + for (isize i = 0; i < gb_count_of(indices); i++) { + i64 a = indices[i]; + for (isize j = i+1; j < gb_count_of(indices); j++) { + i64 b = indices[j]; + if (a > b && b >= 0) { + error(se->close, "Invalid slice indices: [%td > %td]", a, b); + } + } + } + + case_end; + + + case_ast_node(ce, CallExpr, node); + return check_call_expr(c, o, node); + case_end; + + case_ast_node(de, DerefExpr, node); + check_expr_or_type(c, o, de->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } else { + Type *t = base_type(o->type); + if (t->kind == Type_Pointer) { + o->mode = Addressing_Variable; + o->type = t->Pointer.elem; + } else { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot dereference `%s`", str); + gb_string_free(str); + goto error; + } + } + case_end; + + case_ast_node(de, DemaybeExpr, node); + check_expr_or_type(c, o, de->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } else { + Type *t = base_type(o->type); + if (t->kind == Type_Maybe) { + Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); + Type *elem = t->Maybe.elem; + Token tok = make_token_ident(str_lit("")); + variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false); + variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false); + + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = 2; + + o->type = tuple; + o->mode = Addressing_Variable; + } else { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot demaybe `%s`", str); + gb_string_free(str); + goto error; + } + } + case_end; + + case AstNode_ProcType: + case AstNode_PointerType: + case AstNode_MaybeType: + case AstNode_ArrayType: + case AstNode_VectorType: + case AstNode_StructType: + case AstNode_RawUnionType: + o->mode = Addressing_Type; + o->type = check_type(c, node); + break; + } + + kind = Expr_Expr; + o->expr = node; + return kind; + +error: + o->mode = Addressing_Invalid; + o->expr = node; + return kind; +} + +ExprKind check_expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { + ExprKind kind = check__expr_base(c, o, node, type_hint); + Type *type = NULL; + ExactValue value = {ExactValue_Invalid}; + switch (o->mode) { + case Addressing_Invalid: + type = t_invalid; + break; + case Addressing_NoValue: + type = NULL; + break; + case Addressing_Constant: + type = o->type; + value = o->value; + break; + default: + type = o->type; + break; + } + + if (type != NULL && is_type_untyped(type)) { + add_untyped(&c->info, node, false, o->mode, type, value); + } else { + add_type_and_value(&c->info, node, o->mode, type, value); + } + return kind; +} + + +void check_multi_expr(Checker *c, Operand *o, AstNode *e) { + gbString err_str = NULL; + check_expr_base(c, o, e, NULL); + switch (o->mode) { + default: + return; // NOTE(bill): Valid + + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as value", err_str); + break; + case Addressing_Type: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not an expression", err_str); + break; + } + gb_string_free(err_str); + o->mode = Addressing_Invalid; +} + +void check_not_tuple(Checker *c, Operand *o) { + if (o->mode == Addressing_Value) { + // NOTE(bill): Tuples are not first class thus never named + if (o->type->kind == Type_Tuple) { + isize count = o->type->Tuple.variable_count; + GB_ASSERT(count != 1); + error(ast_node_token(o->expr), + "%td-valued tuple found where single value expected", count); + o->mode = Addressing_Invalid; + } + } +} + +void check_expr(Checker *c, Operand *o, AstNode *e) { + check_multi_expr(c, o, e); + check_not_tuple(c, o); +} + + +void check_expr_or_type(Checker *c, Operand *o, AstNode *e) { + check_expr_base(c, o, e, NULL); + check_not_tuple(c, o); + if (o->mode == Addressing_NoValue) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), + "`%s` used as value or type", str); + o->mode = Addressing_Invalid; + gb_string_free(str); + } +} + + +gbString write_expr_to_string(gbString str, AstNode *node); + +gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) { + for_array(i, params) { + ast_node(p, Parameter, params.e[i]); + if (i > 0) { + str = gb_string_appendc(str, sep); + } + + str = write_expr_to_string(str, params.e[i]); + } + return str; +} + +gbString string_append_token(gbString str, Token token) { + if (token.string.len > 0) { + return gb_string_append_length(str, token.string.text, token.string.len); + } + return str; +} + + +gbString write_expr_to_string(gbString str, AstNode *node) { + if (node == NULL) + return str; + + if (is_ast_node_stmt(node)) { + GB_ASSERT("stmt passed to write_expr_to_string"); + } + + switch (node->kind) { + default: + str = gb_string_appendc(str, "(BadExpr)"); + break; + + case_ast_node(i, Ident, node); + str = string_append_token(str, *i); + case_end; + + case_ast_node(bl, BasicLit, node); + str = string_append_token(str, *bl); + case_end; + + case_ast_node(pl, ProcLit, node); + str = write_expr_to_string(str, pl->type); + case_end; + + case_ast_node(cl, CompoundLit, node); + str = write_expr_to_string(str, cl->type); + str = gb_string_appendc(str, "{"); + for_array(i, cl->elems) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, cl->elems.e[i]); + } + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(te, TagExpr, node); + str = gb_string_appendc(str, "#"); + str = string_append_token(str, te->name); + str = write_expr_to_string(str, te->expr); + case_end; + + case_ast_node(ue, UnaryExpr, node); + str = string_append_token(str, ue->op); + str = write_expr_to_string(str, ue->expr); + case_end; + + case_ast_node(de, DerefExpr, node); + str = write_expr_to_string(str, de->expr); + str = gb_string_appendc(str, "^"); + case_end; + + case_ast_node(de, DemaybeExpr, node); + str = write_expr_to_string(str, de->expr); + str = gb_string_appendc(str, "?"); + case_end; + + case_ast_node(be, BinaryExpr, node); + str = write_expr_to_string(str, be->left); + str = gb_string_appendc(str, " "); + str = string_append_token(str, be->op); + str = gb_string_appendc(str, " "); + str = write_expr_to_string(str, be->right); + case_end; + + case_ast_node(pe, ParenExpr, node); + str = gb_string_appendc(str, "("); + str = write_expr_to_string(str, pe->expr); + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(se, SelectorExpr, node); + str = write_expr_to_string(str, se->expr); + str = gb_string_appendc(str, "."); + str = write_expr_to_string(str, se->selector); + case_end; + + case_ast_node(ie, IndexExpr, node); + str = write_expr_to_string(str, ie->expr); + str = gb_string_appendc(str, "["); + str = write_expr_to_string(str, ie->index); + str = gb_string_appendc(str, "]"); + case_end; + + case_ast_node(se, SliceExpr, node); + str = write_expr_to_string(str, se->expr); + str = gb_string_appendc(str, "["); + str = write_expr_to_string(str, se->low); + str = gb_string_appendc(str, ":"); + str = write_expr_to_string(str, se->high); + if (se->triple_indexed) { + str = gb_string_appendc(str, ":"); + str = write_expr_to_string(str, se->max); + } + str = gb_string_appendc(str, "]"); + case_end; + + case_ast_node(e, Ellipsis, node); + str = gb_string_appendc(str, ".."); + case_end; + + case_ast_node(fv, FieldValue, node); + str = write_expr_to_string(str, fv->field); + str = gb_string_appendc(str, " = "); + str = write_expr_to_string(str, fv->value); + case_end; + + case_ast_node(pt, PointerType, node); + str = gb_string_appendc(str, "^"); + 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); + str = gb_string_appendc(str, "]"); + str = write_expr_to_string(str, at->elem); + case_end; + + case_ast_node(vt, VectorType, node); + str = gb_string_appendc(str, "{"); + str = write_expr_to_string(str, vt->count); + str = gb_string_appendc(str, "}"); + str = write_expr_to_string(str, vt->elem); + case_end; + + case_ast_node(p, Parameter, node); + if (p->is_using) { + str = gb_string_appendc(str, "using "); + } + for_array(i, p->names) { + AstNode *name = p->names.e[i]; + if (i > 0) + str = gb_string_appendc(str, ", "); + str = write_expr_to_string(str, name); + } + + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, p->type); + case_end; + + case_ast_node(ce, CallExpr, node); + str = write_expr_to_string(str, ce->proc); + str = gb_string_appendc(str, "("); + + for_array(i, ce->args) { + AstNode *arg = ce->args.e[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, arg); + } + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(pt, ProcType, node); + str = gb_string_appendc(str, "proc("); + str = write_params_to_string(str, pt->params, ", "); + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(st, StructType, node); + str = gb_string_appendc(str, "struct "); + if (st->is_packed) str = gb_string_appendc(str, "#packed "); + if (st->is_ordered) str = gb_string_appendc(str, "#ordered "); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(st, RawUnionType, node); + str = gb_string_appendc(str, "raw_union {"); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(st, UnionType, node); + str = gb_string_appendc(str, "union {"); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(et, EnumType, node); + str = gb_string_appendc(str, "enum "); + if (et->base_type != NULL) { + str = write_expr_to_string(str, et->base_type); + str = gb_string_appendc(str, " "); + } + str = gb_string_appendc(str, "{"); + str = gb_string_appendc(str, "}"); + case_end; + } + + return str; +} + +gbString expr_to_string(AstNode *expression) { + return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression); +} diff --git a/src/checker/stmt.c b/src/checker/stmt.c new file mode 100644 index 000000000..ee56c3cd1 --- /dev/null +++ b/src/checker/stmt.c @@ -0,0 +1,1130 @@ +bool check_is_terminating(AstNode *node); +bool check_has_break (AstNode *stmt, bool implicit); +void check_stmt (Checker *c, AstNode *node, u32 flags); + + +// Statements and Declarations +typedef enum StmtFlag { + Stmt_BreakAllowed = GB_BIT(0), + Stmt_ContinueAllowed = GB_BIT(1), + Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough +} StmtFlag; + + + +void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) { + if (stmts.count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + typedef struct { + Entity *e; + DeclInfo *d; + } Delay; + Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count); + Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, stmts.count); + + for_array(i, stmts) { + AstNode *node = stmts.e[i]; + switch (node->kind) { + case_ast_node(cd, ConstDecl, node); + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + ExactValue v = {ExactValue_Invalid}; + + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); + e->identifier = name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = cd->type; + d->init_expr = value; + + add_entity_and_decl_info(c, name, e, d); + + Delay delay = {e, d}; + array_add(&delayed_const, delay); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(node), "Extra initial expression"); + } + case_end; + + case_ast_node(td, TypeDecl, node); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL); + e->identifier = td->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = td->type; + + add_entity_and_decl_info(c, td->name, e, d); + + Delay delay = {e, d}; + array_add(&delayed_type, delay); + case_end; + } + } + + for_array(i, delayed_type) { + check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL); + } + for_array(i, delayed_const) { + check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL); + } + + bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0; + u32 f = flags & (~Stmt_FallthroughAllowed); + + for_array(i, stmts) { + AstNode *n = stmts.e[i]; + if (n->kind == AstNode_EmptyStmt) { + continue; + } + u32 new_flags = f; + if (ft_ok && i+1 == stmts.count) { + new_flags |= Stmt_FallthroughAllowed; + } + check_stmt(c, n, new_flags); + } + + gb_temp_arena_memory_end(tmp); +} + +bool check_is_terminating_list(AstNodeArray stmts) { + + // Iterate backwards + for (isize n = stmts.count-1; n >= 0; n--) { + AstNode *stmt = stmts.e[n]; + if (stmt->kind != AstNode_EmptyStmt) { + return check_is_terminating(stmt); + } + } + + return false; +} + +bool check_has_break_list(AstNodeArray stmts, bool implicit) { + for_array(i, stmts) { + AstNode *stmt = stmts.e[i]; + if (check_has_break(stmt, implicit)) { + return true; + } + } + return false; +} + + +bool check_has_break(AstNode *stmt, bool implicit) { + switch (stmt->kind) { + case AstNode_BranchStmt: + if (stmt->BranchStmt.token.kind == Token_break) { + return implicit; + } + break; + case AstNode_BlockStmt: + return check_has_break_list(stmt->BlockStmt.stmts, implicit); + + case AstNode_IfStmt: + if (check_has_break(stmt->IfStmt.body, implicit) || + (stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) { + return true; + } + break; + + case AstNode_CaseClause: + return check_has_break_list(stmt->CaseClause.stmts, implicit); + } + + return false; +} + + + +// NOTE(bill): The last expression has to be a `return` statement +// TODO(bill): This is a mild hack and should be probably handled properly +// TODO(bill): Warn/err against code after `return` that it won't be executed +bool check_is_terminating(AstNode *node) { + switch (node->kind) { + case_ast_node(rs, ReturnStmt, node); + return true; + case_end; + + case_ast_node(bs, BlockStmt, node); + return check_is_terminating_list(bs->stmts); + case_end; + + case_ast_node(es, ExprStmt, node); + return check_is_terminating(es->expr); + case_end; + + case_ast_node(is, IfStmt, node); + if (is->else_stmt != NULL) { + if (check_is_terminating(is->body) && + check_is_terminating(is->else_stmt)) { + return true; + } + } + case_end; + + case_ast_node(fs, ForStmt, node); + if (fs->cond == NULL && !check_has_break(fs->body, true)) { + return true; + } + case_end; + + case_ast_node(ms, MatchStmt, node); + bool has_default = false; + for_array(i, ms->body->BlockStmt.stmts) { + AstNode *clause = ms->body->BlockStmt.stmts.e[i]; + ast_node(cc, CaseClause, clause); + if (cc->list.count == 0) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; + + case_ast_node(ms, TypeMatchStmt, node); + bool has_default = false; + for_array(i, ms->body->BlockStmt.stmts) { + AstNode *clause = ms->body->BlockStmt.stmts.e[i]; + ast_node(cc, CaseClause, clause); + if (cc->list.count == 0) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; + + case_ast_node(pa, PushAllocator, node); + return check_is_terminating(pa->body); + case_end; + case_ast_node(pc, PushContext, node); + return check_is_terminating(pc->body); + case_end; + } + + return false; +} + +Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { + if (op_a->mode == Addressing_Invalid || + op_a->type == t_invalid) { + return NULL; + } + + AstNode *node = unparen_expr(lhs); + + // NOTE(bill): Ignore assignments to `_` + if (node->kind == AstNode_Ident && + str_eq(node->Ident.string, str_lit("_"))) { + add_entity_definition(&c->info, node, NULL); + check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier")); + if (op_a->mode == Addressing_Invalid) + return NULL; + return op_a->type; + } + + Entity *e = NULL; + bool used = false; + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + e = scope_lookup_entity(c->context.scope, i->string); + if (e != NULL && e->kind == Entity_Variable) { + used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case + } + } + + + Operand op_b = {Addressing_Invalid}; + check_expr(c, &op_b, lhs); + if (e) { + e->flags |= EntityFlag_Used*used; + } + + if (op_b.mode == Addressing_Invalid || + op_b.type == t_invalid) { + return NULL; + } + + switch (op_b.mode) { + case Addressing_Invalid: + return NULL; + case Addressing_Variable: + break; + default: { + if (op_b.expr->kind == AstNode_SelectorExpr) { + // NOTE(bill): Extra error checks + Operand op_c = {Addressing_Invalid}; + ast_node(se, SelectorExpr, op_b.expr); + check_expr(c, &op_c, se->expr); + } + + gbString str = expr_to_string(op_b.expr); + switch (op_b.mode) { + case Addressing_Value: + error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); + break; + default: + error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); + break; + } + gb_string_free(str); + } break; + } + + check_assignment(c, op_a, op_b.type, str_lit("assignment")); + if (op_a->mode == Addressing_Invalid) { + return NULL; + } + + return op_a->type; +} + +bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { + if (is_type_pointer(type)) { + *is_union_ptr = is_type_union(type_deref(type)); + return *is_union_ptr; + } + if (is_type_any(type)) { + *is_any = true; + return *is_any; + } + return false; +} + +void check_stmt_internal(Checker *c, AstNode *node, u32 flags); +void check_stmt(Checker *c, AstNode *node, u32 flags) { + u32 prev_stmt_state_flags = c->context.stmt_state_flags; + + if (node->stmt_state_flags != 0) { + u32 in = node->stmt_state_flags; + u32 out = c->context.stmt_state_flags; + + if (in & StmtStateFlag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & StmtStateFlag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + + c->context.stmt_state_flags = out; + } + + check_stmt_internal(c, node, flags); + + c->context.stmt_state_flags = prev_stmt_state_flags; +} + +typedef struct TypeAndToken { + Type *type; + Token token; +} TypeAndToken; + +#define MAP_TYPE TypeAndToken +#define MAP_PROC map_type_and_token_ +#define MAP_NAME MapTypeAndToken +#include "../map.c" + +void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { + u32 mod_flags = flags & (~Stmt_FallthroughAllowed); + switch (node->kind) { + case_ast_node(_, EmptyStmt, node); case_end; + case_ast_node(_, BadStmt, node); case_end; + case_ast_node(_, BadDecl, node); case_end; + + case_ast_node(es, ExprStmt, node) + Operand operand = {Addressing_Invalid}; + ExprKind kind = check_expr_base(c, &operand, es->expr, NULL); + switch (operand.mode) { + case Addressing_Type: + error(ast_node_token(node), "Is not an expression"); + break; + case Addressing_NoValue: + return; + default: { + if (kind == Expr_Stmt) { + return; + } + if (operand.expr->kind == AstNode_CallExpr) { + return; + } + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(node), "Expression is not used: `%s`", expr_str); + gb_string_free(expr_str); + } break; + } + case_end; + + case_ast_node(ts, TagStmt, node); + // TODO(bill): Tag Statements + error(ast_node_token(node), "Tag statements are not supported yet"); + check_stmt(c, ts->stmt, flags); + case_end; + + case_ast_node(ids, IncDecStmt, node); + Token op = ids->op; + switch (ids->op.kind) { + case Token_Increment: + op.kind = Token_Add; + op.string.len = 1; + break; + case Token_Decrement: + op.kind = Token_Sub; + op.string.len = 1; + break; + default: + error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string)); + return; + } + + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, ids->expr); + if (operand.mode == Addressing_Invalid) + return; + if (!is_type_numeric(operand.type)) { + error(ids->op, "Non numeric type"); + return; + } + + AstNode basic_lit = {AstNode_BasicLit}; + ast_node(bl, BasicLit, &basic_lit); + *bl = ids->op; + bl->kind = Token_Integer; + bl->string = str_lit("1"); + + AstNode binary_expr = {AstNode_BinaryExpr}; + ast_node(be, BinaryExpr, &binary_expr); + be->op = op; + be->left = ids->expr; + be->right = &basic_lit; + check_binary_expr(c, &operand, &binary_expr); + case_end; + + case_ast_node(as, AssignStmt, node); + switch (as->op.kind) { + case Token_Eq: { + // a, b, c = 1, 2, 3; // Multisided + if (as->lhs.count == 0) { + error(as->op, "Missing lhs in assignment statement"); + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be + // an extra allocation + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count); + + for_array(i, as->rhs) { + AstNode *rhs = as->rhs.e[i]; + Operand o = {0}; + check_multi_expr(c, &o, rhs); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + isize lhs_count = as->lhs.count; + isize rhs_count = operands.count; + + isize operand_count = gb_min(as->lhs.count, operands.count); + for (isize i = 0; i < operand_count; i++) { + AstNode *lhs = as->lhs.e[i]; + check_assignment_variable(c, &operands.e[i], lhs); + } + if (lhs_count != rhs_count) { + error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); + } + + gb_temp_arena_memory_end(tmp); + } break; + + default: { + // a += 1; // Single-sided + Token op = as->op; + if (as->lhs.count != 1 || as->rhs.count != 1) { + error(op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string)); + return; + } + if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) { + error(op, "Unknown Assignment operation `%.*s`", LIT(op.string)); + return; + } + // TODO(bill): Check if valid assignment operator + Operand operand = {Addressing_Invalid}; + AstNode binary_expr = {AstNode_BinaryExpr}; + ast_node(be, BinaryExpr, &binary_expr); + be->op = op; + be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); + // NOTE(bill): Only use the first one will be used + be->left = as->lhs.e[0]; + be->right = as->rhs.e[0]; + + check_binary_expr(c, &operand, &binary_expr); + if (operand.mode == Addressing_Invalid) { + return; + } + // NOTE(bill): Only use the first one will be used + check_assignment_variable(c, &operand, as->lhs.e[0]); + } break; + } + case_end; + + case_ast_node(bs, BlockStmt, node); + check_open_scope(c, node); + check_stmt_list(c, bs->stmts, mod_flags); + check_close_scope(c); + case_end; + + case_ast_node(is, IfStmt, node); + check_open_scope(c, node); + + if (is->init != NULL) { + check_stmt(c, is->init, 0); + } + + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, is->cond); + if (operand.mode != Addressing_Invalid && + !is_type_boolean(operand.type)) { + error(ast_node_token(is->cond), + "Non-boolean condition in `if` statement"); + } + + check_stmt(c, is->body, mod_flags); + + if (is->else_stmt) { + switch (is->else_stmt->kind) { + case AstNode_IfStmt: + case AstNode_BlockStmt: + check_stmt(c, is->else_stmt, mod_flags); + break; + default: + error(ast_node_token(is->else_stmt), + "Invalid `else` statement in `if` statement"); + break; + } + } + + check_close_scope(c); + case_end; + + case_ast_node(rs, ReturnStmt, node); + GB_ASSERT(c->proc_stack.count > 0); + + if (c->in_defer) { + error(rs->token, "You cannot `return` within a defer statement"); + // TODO(bill): Should I break here? + break; + } + + + Type *proc_type = c->proc_stack.e[c->proc_stack.count-1]; + isize result_count = 0; + if (proc_type->Proc.results) { + result_count = proc_type->Proc.results->Tuple.variable_count; + } + + if (result_count > 0) { + Entity **variables = NULL; + if (proc_type->Proc.results != NULL) { + TypeTuple *tuple = &proc_type->Proc.results->Tuple; + variables = tuple->variables; + } + if (rs->results.count == 0) { + error(ast_node_token(node), "Expected %td return values, got 0", result_count); + } else { + check_init_variables(c, variables, result_count, + rs->results, str_lit("return statement")); + } + } else if (rs->results.count > 0) { + error(ast_node_token(rs->results.e[0]), "No return values expected"); + } + case_end; + + case_ast_node(fs, ForStmt, node); + u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; + check_open_scope(c, node); + + if (fs->init != NULL) { + check_stmt(c, fs->init, 0); + } + if (fs->cond) { + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, fs->cond); + if (operand.mode != Addressing_Invalid && + !is_type_boolean(operand.type)) { + error(ast_node_token(fs->cond), + "Non-boolean condition in `for` statement"); + } + } + if (fs->post != NULL) { + check_stmt(c, fs->post, 0); + } + check_stmt(c, fs->body, new_flags); + + check_close_scope(c); + case_end; + + case_ast_node(ms, MatchStmt, node); + Operand x = {0}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + + if (ms->init != NULL) { + check_stmt(c, ms->init, 0); + } + if (ms->tag != NULL) { + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, str_lit("match expression")); + } else { + x.mode = Addressing_Constant; + x.type = t_bool; + x.value = make_exact_value_bool(true); + + Token token = {0}; + token.pos = ast_node_token(ms->body).pos; + token.string = str_lit("true"); + x.expr = make_ident(c->curr_ast_file, token); + } + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(cc, CaseClause, stmt); + if (cc->list.count == 0) { + default_stmt = stmt; + } + } else { + error(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(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; + } + } + } +; + + MapTypeAndToken seen = {0}; // NOTE(bill): Multimap + map_type_and_token_init(&seen, heap_allocator()); + + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + + for_array(j, cc->list) { + AstNode *expr = cc->list.e[j]; + Operand y = {0}; + Operand z = {0}; + Token eq = {Token_CmpEq}; + + check_expr(c, &y, expr); + if (x.mode == Addressing_Invalid || + y.mode == Addressing_Invalid) { + continue; + } + convert_to_typed(c, &y, x.type, 0); + if (y.mode == Addressing_Invalid) { + continue; + } + + z = y; + check_comparison(c, &z, &x, eq); + if (z.mode == Addressing_Invalid) { + continue; + } + if (y.mode != Addressing_Constant) { + continue; + } + + if (y.value.kind != ExactValue_Invalid) { + HashKey key = hash_exact_value(y.value); + TypeAndToken *found = map_type_and_token_get(&seen, key); + if (found != NULL) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + isize count = map_type_and_token_multi_count(&seen, key); + TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); + + map_type_and_token_multi_get_all(&seen, key, taps); + bool continue_outer = false; + + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (are_types_identical(y.type, tap.type)) { + TokenPos pos = tap.token.pos; + gbString expr_str = expr_to_string(y.expr); + error(ast_node_token(y.expr), + "Duplicate case `%s`\n" + "\tprevious case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + continue_outer = true; + break; + } + } + + gb_temp_arena_memory_end(tmp); + + if (continue_outer) { + continue; + } + } + TypeAndToken tap = {y.type, ast_node_token(y.expr)}; + map_type_and_token_multi_insert(&seen, key, tap); + } + } + + check_open_scope(c, stmt); + u32 ft_flags = mod_flags; + if (i+1 < bs->stmts.count) { + ft_flags |= Stmt_FallthroughAllowed; + } + check_stmt_list(c, cc->stmts, ft_flags); + check_close_scope(c); + } + + map_type_and_token_destroy(&seen); + + check_close_scope(c); + case_end; + + case_ast_node(ms, TypeMatchStmt, node); + Operand x = {0}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + + bool is_union_ptr = false; + bool is_any = false; + + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, str_lit("type match expression")); + if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) { + gbString str = type_to_string(x.type); + error(ast_node_token(x.expr), + "Invalid type for this type match expression, got `%s`", str); + gb_string_free(str); + break; + } + + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(cc, CaseClause, stmt); + if (cc->list.count == 0) { + default_stmt = stmt; + } + } else { + error(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(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; + } + + + MapBool seen = {0}; + map_bool_init(&seen, heap_allocator()); + + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + // TODO(bill): Make robust + Type *bt = base_type(type_deref(x.type)); + + + AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL; + Type *case_type = NULL; + if (type_expr != NULL) { // Otherwise it's a default expression + Operand y = {0}; + check_expr_or_type(c, &y, type_expr); + + if (is_union_ptr) { + GB_ASSERT(is_type_union(bt)); + bool tag_type_found = false; + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->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); + error(ast_node_token(y.expr), + "Unknown tag type, got `%s`", type_str); + gb_string_free(type_str); + continue; + } + case_type = y.type; + } else if (is_any) { + case_type = y.type; + } else { + GB_PANIC("Unknown type to type match statement"); + } + + HashKey key = hash_pointer(y.type); + bool *found = map_bool_get(&seen, key); + if (found) { + TokenPos pos = cc->token.pos; + gbString expr_str = expr_to_string(y.expr); + error(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_bool_set(&seen, key, cast(bool)true); + } + + check_open_scope(c, stmt); + if (case_type != NULL) { + add_type_info_type(c, case_type); + + // NOTE(bill): Dummy type + Type *tt = case_type; + if (is_union_ptr) { + tt = make_type_pointer(c->allocator, case_type); + add_type_info_type(c, tt); + } + Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt); + tag_var->flags |= EntityFlag_Used; + add_entity(c, c->context.scope, ms->var, tag_var); + add_entity_use(c, ms->var, tag_var); + } + check_stmt_list(c, cc->stmts, mod_flags); + check_close_scope(c); + } + map_bool_destroy(&seen); + + check_close_scope(c); + case_end; + + + case_ast_node(ds, DeferStmt, node); + if (is_ast_node_decl(ds->stmt)) { + error(ds->token, "You cannot defer a declaration"); + } else { + bool out_in_defer = c->in_defer; + c->in_defer = true; + check_stmt(c, ds->stmt, 0); + c->in_defer = out_in_defer; + } + case_end; + + case_ast_node(bs, BranchStmt, node); + Token token = bs->token; + switch (token.kind) { + case Token_break: + if ((flags & Stmt_BreakAllowed) == 0) { + error(token, "`break` only allowed in `for` or `match` statements"); + } + break; + case Token_continue: + if ((flags & Stmt_ContinueAllowed) == 0) { + error(token, "`continue` only allowed in `for` statements"); + } + break; + case Token_fallthrough: + if ((flags & Stmt_FallthroughAllowed) == 0) { + error(token, "`fallthrough` statement in illegal position"); + } + break; + default: + error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); + break; + } + case_end; + + case_ast_node(us, UsingStmt, node); + switch (us->node->kind) { + case_ast_node(es, ExprStmt, us->node); + // TODO(bill): Allow for just a LHS expression list rather than this silly code + Entity *e = NULL; + + bool is_selector = false; + AstNode *expr = unparen_expr(es->expr); + if (expr->kind == AstNode_Ident) { + String name = expr->Ident.string; + e = scope_lookup_entity(c->context.scope, name); + } else if (expr->kind == AstNode_SelectorExpr) { + Operand o = {0}; + e = check_selector(c, &o, expr); + is_selector = true; + } + + if (e == NULL) { + error(us->token, "`using` applied to an unknown entity"); + return; + } + + switch (e->kind) { + case Entity_TypeName: { + Type *t = base_type(e->type); + if (is_type_struct(t) || is_type_enum(t)) { + for (isize i = 0; i < t->Record.other_field_count; i++) { + Entity *f = t->Record.other_fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + } else if (is_type_union(t)) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + for (isize i = 0; i < t->Record.other_field_count; i++) { + Entity *f = t->Record.other_fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + } + } break; + + case Entity_ImportName: { + Scope *scope = e->ImportName.scope; + for_array(i, scope->elements.entries) { + Entity *decl = scope->elements.entries.e[i].value; + Entity *found = scope_insert_entity(c->context.scope, decl); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, + "Namespace collision while `using` `%s` of: %.*s\n" + "\tat %.*s(%td:%td)\n" + "\tat %.*s(%td:%td)", + expr_str, LIT(found->token.string), + LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column, + LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column + ); + gb_string_free(expr_str); + return; + } + } + } break; + + case Entity_Variable: { + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + if (is_selector) { + uvar->using_expr = expr; + } + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string)); + gb_string_free(expr_str); + return; + } + } + } + } else { + error(us->token, "`using` can only be applied to variables of type struct or raw_union"); + return; + } + } 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?"); + } + case_end; + + case_ast_node(vd, VarDecl, us->node); + if (vd->names.count > 1 && vd->type != NULL) { + error(us->token, "`using` can only be applied to one variable of the same type"); + } + check_var_decl_node(c, us->node); + + for_array(name_index, vd->names) { + AstNode *item = vd->names.e[name_index]; + ast_node(i, Ident, item); + String name = i->string; + Entity *e = scope_lookup_entity(c->context.scope, name); + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + return; + } + } + } + } else { + error(us->token, "`using` can only be applied to variables of type struct or raw_union"); + return; + } + } + case_end; + + + default: + error(us->token, "Invalid AST: Using Statement"); + break; + } + case_end; + + + + case_ast_node(pa, PushAllocator, node); + Operand op = {0}; + check_expr(c, &op, pa->expr); + check_assignment(c, &op, t_allocator, str_lit("argument to push_allocator")); + check_stmt(c, pa->body, mod_flags); + case_end; + + + case_ast_node(pa, PushContext, node); + Operand op = {0}; + check_expr(c, &op, pa->expr); + check_assignment(c, &op, t_context, str_lit("argument to push_context")); + check_stmt(c, pa->body, mod_flags); + case_end; + + + + + + + case_ast_node(vd, VarDecl, node); + check_var_decl_node(c, node); + case_end; + + case_ast_node(cd, ConstDecl, node); + // NOTE(bill): Handled elsewhere + case_end; + + case_ast_node(td, TypeDecl, node); + // NOTE(bill): Handled elsewhere + case_end; + + case_ast_node(pd, ProcDecl, node); + // NOTE(bill): This must be handled here so it has access to the parent scope stuff + // e.g. using + Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL); + e->identifier = pd->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->proc_decl = node; + + add_entity_and_decl_info(c, pd->name, e, d); + check_entity_decl(c, e, d, NULL, NULL); + case_end; + } +} diff --git a/src/checker/types.c b/src/checker/types.c new file mode 100644 index 000000000..f51cbb660 --- /dev/null +++ b/src/checker/types.c @@ -0,0 +1,1487 @@ +typedef struct Scope Scope; + +typedef enum BasicKind { + Basic_Invalid, + Basic_bool, + Basic_i8, + Basic_u8, + Basic_i16, + Basic_u16, + Basic_i32, + Basic_u32, + Basic_i64, + Basic_u64, + Basic_i128, + Basic_u128, + // Basic_f16, + Basic_f32, + Basic_f64, + // Basic_f128, + Basic_int, + Basic_uint, + Basic_rawptr, + Basic_string, // ^u8 + int + Basic_any, // ^Type_Info + rawptr + + Basic_UntypedBool, + Basic_UntypedInteger, + Basic_UntypedFloat, + Basic_UntypedString, + Basic_UntypedRune, + Basic_UntypedNil, + + Basic_Count, + + Basic_byte = Basic_u8, + Basic_rune = Basic_i32, +} BasicKind; + +typedef enum BasicFlag { + BasicFlag_Boolean = GB_BIT(0), + BasicFlag_Integer = GB_BIT(1), + BasicFlag_Unsigned = GB_BIT(2), + BasicFlag_Float = GB_BIT(3), + BasicFlag_Pointer = GB_BIT(4), + BasicFlag_String = GB_BIT(5), + BasicFlag_Rune = GB_BIT(6), + BasicFlag_Untyped = GB_BIT(7), + + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float, + BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer, + BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune, +} BasicFlag; + +typedef struct BasicType { + BasicKind kind; + u32 flags; + i64 size; // -1 if arch. dep. + String name; +} BasicType; + +typedef enum TypeRecordKind { + TypeRecord_Invalid, + + TypeRecord_Struct, + TypeRecord_Enum, + TypeRecord_RawUnion, + TypeRecord_Union, // Tagged + + TypeRecord_Count, +} TypeRecordKind; + +typedef struct TypeRecord { + TypeRecordKind kind; + + // All record types + // Theses are arrays + Entity **fields; // Entity_Variable (otherwise Entity_TypeName if union) + i32 field_count; // == offset_count is struct + AstNode *node; + + union { // NOTE(bill): Reduce size_of Type + struct { // enum only + Type * enum_base; // Default is `int` + Entity * enum_count; + Entity * min_value; + Entity * max_value; + }; + struct { // struct only + i64 * struct_offsets; + bool struct_are_offsets_set; + bool struct_is_packed; + bool struct_is_ordered; + Entity **fields_in_src_order; // Entity_Variable + }; + }; + + // Entity_Constant or Entity_TypeName + Entity **other_fields; + i32 other_field_count; +} TypeRecord; + +#define TYPE_KINDS \ + TYPE_KIND(Basic, BasicType) \ + TYPE_KIND(Pointer, struct { Type *elem; }) \ + TYPE_KIND(Array, struct { Type *elem; i64 count; }) \ + TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \ + TYPE_KIND(Slice, struct { Type *elem; }) \ + TYPE_KIND(Maybe, struct { Type *elem; }) \ + TYPE_KIND(Record, TypeRecord) \ + TYPE_KIND(Named, struct { \ + String name; \ + Type * base; \ + Entity *type_name; /* Entity_TypeName */ \ + }) \ + TYPE_KIND(Tuple, struct { \ + Entity **variables; /* Entity_Variable */ \ + i32 variable_count; \ + bool are_offsets_set; \ + i64 * offsets; \ + }) \ + TYPE_KIND(Proc, struct { \ + Scope *scope; \ + Type * params; /* Type_Tuple */ \ + Type * results; /* Type_Tuple */ \ + i32 param_count; \ + i32 result_count; \ + bool variadic; \ + }) + +typedef enum TypeKind { + Type_Invalid, +#define TYPE_KIND(k, ...) GB_JOIN2(Type_, k), + TYPE_KINDS +#undef TYPE_KIND + Type_Count, +} TypeKind; + +String const type_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")}, +#define TYPE_KIND(k, ...) {cast(u8 *)#k, gb_size_of(#k)-1}, + TYPE_KINDS +#undef TYPE_KIND +}; + +#define TYPE_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(Type, k); + TYPE_KINDS +#undef TYPE_KIND + +typedef struct Type { + TypeKind kind; + union { +#define TYPE_KIND(k, ...) GB_JOIN2(Type, k) k; + TYPE_KINDS +#undef TYPE_KIND + }; +} Type; + +// NOTE(bill): Internal sizes of certain types +// string: 2*word_size (ptr+len) +// slice: 3*word_size (ptr+len+cap) +// array: count*size_of(elem) aligned + +// NOTE(bill): Alignment of structures and other types are to be compatible with C + +typedef struct BaseTypeSizes { + i64 word_size; + i64 max_align; +} BaseTypeSizes; + +typedef Array(isize) Array_isize; + +typedef struct Selection { + Entity * entity; + Array_isize index; + bool indirect; // Set if there was a pointer deref anywhere down the line +} Selection; +Selection empty_selection = {0}; + +Selection make_selection(Entity *entity, Array_isize index, bool indirect) { + Selection s = {entity, index, indirect}; + return s; +} + +void selection_add_index(Selection *s, isize index) { + // IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form + // of heap allocation + if (s->index.e == NULL) { + array_init(&s->index, heap_allocator()); + } + array_add(&s->index, index); +} + + + +#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1} +gb_global Type basic_types[] = { + {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, + {Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}}, + {Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}}, + {Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}}, + {Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}}, + {Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}}, + {Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}}, + {Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}}, + {Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}}, + {Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}}, + {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, + {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, + // {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}}, + {Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}}, + {Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}}, + // {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}}, + {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, + {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, + {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, + {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, + {Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}}, + {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, + {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, + {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, + {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, + {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, + {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, +}; + +gb_global Type basic_type_aliases[] = { + {Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("byte")}}, + {Type_Basic, {Basic_rune, BasicFlag_Integer, 4, STR_LIT("rune")}}, +}; + +gb_global Type *t_invalid = &basic_types[Basic_Invalid]; +gb_global Type *t_bool = &basic_types[Basic_bool]; +gb_global Type *t_i8 = &basic_types[Basic_i8]; +gb_global Type *t_u8 = &basic_types[Basic_u8]; +gb_global Type *t_i16 = &basic_types[Basic_i16]; +gb_global Type *t_u16 = &basic_types[Basic_u16]; +gb_global Type *t_i32 = &basic_types[Basic_i32]; +gb_global Type *t_u32 = &basic_types[Basic_u32]; +gb_global Type *t_i64 = &basic_types[Basic_i64]; +gb_global Type *t_u64 = &basic_types[Basic_u64]; +gb_global Type *t_i128 = &basic_types[Basic_i128]; +gb_global Type *t_u128 = &basic_types[Basic_u128]; +// gb_global Type *t_f16 = &basic_types[Basic_f16]; +gb_global Type *t_f32 = &basic_types[Basic_f32]; +gb_global Type *t_f64 = &basic_types[Basic_f64]; +// gb_global Type *t_f128 = &basic_types[Basic_f128]; +gb_global Type *t_int = &basic_types[Basic_int]; +gb_global Type *t_uint = &basic_types[Basic_uint]; +gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; +gb_global Type *t_string = &basic_types[Basic_string]; +gb_global Type *t_any = &basic_types[Basic_any]; +gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; +gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; +gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; +gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; +gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; +gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; +gb_global Type *t_byte = &basic_type_aliases[0]; +gb_global Type *t_rune = &basic_type_aliases[1]; + + +gb_global Type *t_u8_ptr = NULL; +gb_global Type *t_int_ptr = NULL; + +gb_global Type *t_type_info = NULL; +gb_global Type *t_type_info_ptr = NULL; +gb_global Type *t_type_info_member = NULL; +gb_global Type *t_type_info_member_ptr = NULL; + +gb_global Type *t_type_info_named = NULL; +gb_global Type *t_type_info_integer = NULL; +gb_global Type *t_type_info_float = NULL; +gb_global Type *t_type_info_any = NULL; +gb_global Type *t_type_info_string = NULL; +gb_global Type *t_type_info_boolean = NULL; +gb_global Type *t_type_info_pointer = NULL; +gb_global Type *t_type_info_maybe = NULL; +gb_global Type *t_type_info_procedure = NULL; +gb_global Type *t_type_info_array = NULL; +gb_global Type *t_type_info_slice = NULL; +gb_global Type *t_type_info_vector = NULL; +gb_global Type *t_type_info_tuple = NULL; +gb_global Type *t_type_info_struct = NULL; +gb_global Type *t_type_info_union = NULL; +gb_global Type *t_type_info_raw_union = NULL; +gb_global Type *t_type_info_enum = NULL; + +gb_global Type *t_allocator = NULL; +gb_global Type *t_allocator_ptr = NULL; +gb_global Type *t_context = NULL; +gb_global Type *t_context_ptr = NULL; + + + + + + +gbString type_to_string(Type *type); + +Type *base_type(Type *t) { + for (;;) { + if (t == NULL || t->kind != Type_Named) { + break; + } + t = t->Named.base; + } + return t; +} + +void set_base_type(Type *t, Type *base) { + if (t && t->kind == Type_Named) { + t->Named.base = base; + } +} + + +Type *alloc_type(gbAllocator a, TypeKind kind) { + Type *t = gb_alloc_item(a, Type); + t->kind = kind; + return t; +} + + +Type *make_type_basic(gbAllocator a, BasicType basic) { + Type *t = alloc_type(a, Type_Basic); + t->Basic = basic; + return t; +} + +Type *make_type_pointer(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Pointer); + t->Pointer.elem = elem; + return t; +} + +Type *make_type_maybe(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Maybe); + t->Maybe.elem = elem; + return t; +} + +Type *make_type_array(gbAllocator a, Type *elem, i64 count) { + Type *t = alloc_type(a, Type_Array); + t->Array.elem = elem; + t->Array.count = count; + return t; +} + +Type *make_type_vector(gbAllocator a, Type *elem, i64 count) { + Type *t = alloc_type(a, Type_Vector); + t->Vector.elem = elem; + t->Vector.count = count; + return t; +} + +Type *make_type_slice(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Slice); + t->Array.elem = elem; + return t; +} + + +Type *make_type_struct(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Struct; + return t; +} + +Type *make_type_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Union; + return t; +} + +Type *make_type_raw_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_RawUnion; + return t; +} + +Type *make_type_enum(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Enum; + return t; +} + + + +Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) { + Type *t = alloc_type(a, Type_Named); + t->Named.name = name; + t->Named.base = base; + t->Named.type_name = type_name; + return t; +} + +Type *make_type_tuple(gbAllocator a) { + Type *t = alloc_type(a, Type_Tuple); + return t; +} + +Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, bool variadic) { + Type *t = alloc_type(a, Type_Proc); + + if (variadic) { + if (param_count == 0) { + GB_PANIC("variadic procedure must have at least one parameter"); + } + GB_ASSERT(params != NULL && params->kind == Type_Tuple); + Entity *e = params->Tuple.variables[param_count-1]; + if (base_type(e->type)->kind != Type_Slice) { + // NOTE(bill): For custom calling convention + GB_PANIC("variadic parameter must be of type slice"); + } + } + + t->Proc.scope = scope; + t->Proc.params = params; + t->Proc.param_count = param_count; + t->Proc.results = results; + t->Proc.result_count = result_count; + t->Proc.variadic = variadic; + return t; +} + + +Type *type_deref(Type *t) { + if (t != NULL) { + Type *bt = base_type(t); + if (bt == NULL) + return NULL; + if (bt != NULL && bt->kind == Type_Pointer) + return bt->Pointer.elem; + } + return t; +} + +Type *get_enum_base_type(Type *t) { + Type *bt = base_type(t); + if (bt->kind == Type_Record && bt->Record.kind == TypeRecord_Enum) { + GB_ASSERT(bt->Record.enum_base != NULL); + return bt->Record.enum_base; + } + return t; +} + +bool is_type_named(Type *t) { + if (t->kind == Type_Basic) { + return true; + } + return t->kind == Type_Named; +} +bool is_type_boolean(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Boolean) != 0; + } + return false; +} +bool is_type_integer(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Integer) != 0; + } + return false; +} +bool is_type_unsigned(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Unsigned) != 0; + } + return false; +} +bool is_type_numeric(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Numeric) != 0; + } + if (t->kind == Type_Vector) { + return is_type_numeric(t->Vector.elem); + } + return false; +} +bool is_type_string(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_String) != 0; + } + return false; +} +bool is_type_typed(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Untyped) == 0; + } + return true; +} +bool is_type_untyped(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Untyped) != 0; + } + return false; +} +bool is_type_ordered(Type *t) { + t = base_type(get_enum_base_type(t)); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Ordered) != 0; + } + if (t->kind == Type_Pointer) { + return true; + } + return false; +} +bool is_type_constant_type(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_ConstantType) != 0; + } + if (t->kind == Type_Record) { + return t->Record.kind == TypeRecord_Enum; + } + return false; +} +bool is_type_float(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Float) != 0; + } + return false; +} +bool is_type_f32(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f32; + } + return false; +} +bool is_type_f64(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f64; + } + return false; +} +bool is_type_pointer(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Pointer) != 0; + } + return t->kind == Type_Pointer; +} +bool is_type_maybe(Type *t) { + t = base_type(t); + return t->kind == Type_Maybe; +} +bool is_type_tuple(Type *t) { + t = base_type(t); + return t->kind == Type_Tuple; +} + + +bool is_type_int_or_uint(Type *t) { + if (t->kind == Type_Basic) { + return (t->Basic.kind == Basic_int) || (t->Basic.kind == Basic_uint); + } + return false; +} +bool is_type_rawptr(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_rawptr; + } + return false; +} +bool is_type_u8(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_u8; + } + return false; +} +bool is_type_array(Type *t) { + t = base_type(t); + return t->kind == Type_Array; +} +bool is_type_slice(Type *t) { + t = base_type(t); + return t->kind == Type_Slice; +} +bool is_type_u8_slice(Type *t) { + t = base_type(t); + if (t->kind == Type_Slice) { + return is_type_u8(t->Slice.elem); + } + return false; +} +bool is_type_vector(Type *t) { + t = base_type(t); + return t->kind == Type_Vector; +} +bool is_type_proc(Type *t) { + t = base_type(t); + return t->kind == Type_Proc; +} +Type *base_vector_type(Type *t) { + if (is_type_vector(t)) { + t = base_type(t); + return t->Vector.elem; + } + return t; +} + + +bool is_type_enum(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum); +} +bool is_type_struct(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct); +} +bool is_type_union(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union); +} +bool is_type_raw_union(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion); +} + +bool is_type_any(Type *t) { + t = base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_any); +} +bool is_type_untyped_nil(Type *t) { + t = base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil); +} + + + +bool is_type_indexable(Type *t) { + return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t); +} + + +bool type_has_nil(Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Basic: + return is_type_rawptr(t); + + case Type_Tuple: + return false; + + case Type_Record: + switch (t->Record.kind) { + case TypeRecord_Enum: + return false; + } + break; + } + return true; +} + + +bool is_type_comparable(Type *t) { + t = base_type(get_enum_base_type(t)); + switch (t->kind) { + case Type_Basic: + return t->kind != Basic_UntypedNil; + case Type_Pointer: + return true; + case Type_Record: { + if (false && is_type_struct(t)) { + // TODO(bill): Should I even allow this? + for (isize i = 0; i < t->Record.field_count; i++) { + if (!is_type_comparable(t->Record.fields[i]->type)) + return false; + } + } else if (is_type_enum(t)) { + return is_type_comparable(t->Record.enum_base); + } + return false; + } break; + case Type_Array: + return is_type_comparable(t->Array.elem); + case Type_Vector: + return is_type_comparable(t->Vector.elem); + case Type_Proc: + return true; + } + return false; +} + +bool are_types_identical(Type *x, Type *y) { + if (x == y) + return true; + + if ((x == NULL && y != NULL) || + (x != NULL && y == NULL)) { + return false; + } + + switch (x->kind) { + case Type_Basic: + if (y->kind == Type_Basic) { + return x->Basic.kind == y->Basic.kind; + } + break; + + case Type_Array: + if (y->kind == Type_Array) { + return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem); + } + break; + + case Type_Vector: + if (y->kind == Type_Vector) { + return (x->Vector.count == y->Vector.count) && are_types_identical(x->Vector.elem, y->Vector.elem); + } + break; + + case Type_Slice: + if (y->kind == Type_Slice) { + return are_types_identical(x->Slice.elem, y->Slice.elem); + } + break; + + case Type_Record: + if (y->kind == Type_Record) { + if (x->Record.kind == y->Record.kind) { + switch (x->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + case TypeRecord_Union: + if (x->Record.field_count == y->Record.field_count && + x->Record.struct_is_packed == y->Record.struct_is_packed && + x->Record.struct_is_ordered == y->Record.struct_is_ordered) { + for (isize i = 0; i < x->Record.field_count; i++) { + if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) { + return false; + } + if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) { + return false; + } + } + return true; + } + break; + + case TypeRecord_Enum: + // NOTE(bill): Each enum is unique + return x == y; + } + } + } + break; + + case Type_Pointer: + if (y->kind == Type_Pointer) { + return are_types_identical(x->Pointer.elem, y->Pointer.elem); + } + break; + + case Type_Maybe: + if (y->kind == Type_Maybe) { + return are_types_identical(x->Maybe.elem, y->Maybe.elem); + } + break; + + case Type_Named: + if (y->kind == Type_Named) { + return x->Named.base == y->Named.base; + } + break; + + case Type_Tuple: + if (y->kind == Type_Tuple) { + if (x->Tuple.variable_count == y->Tuple.variable_count) { + for (isize i = 0; i < x->Tuple.variable_count; i++) { + if (!are_types_identical(x->Tuple.variables[i]->type, y->Tuple.variables[i]->type)) { + return false; + } + } + return true; + } + } + break; + + case Type_Proc: + if (y->kind == Type_Proc) { + return are_types_identical(x->Proc.params, y->Proc.params) && + are_types_identical(x->Proc.results, y->Proc.results); + } + break; + } + + + return false; +} + + +Type *default_type(Type *type) { + if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_UntypedBool: return t_bool; + case Basic_UntypedInteger: return t_int; + case Basic_UntypedFloat: return t_f64; + case Basic_UntypedString: return t_string; + case Basic_UntypedRune: return t_rune; + } + } + return type; +} + + + + +gb_global Entity *entity__any_type_info = NULL; +gb_global Entity *entity__any_data = NULL; +gb_global Entity *entity__string_data = NULL; +gb_global Entity *entity__string_count = NULL; +gb_global Entity *entity__slice_count = NULL; +gb_global Entity *entity__slice_capacity = NULL; + +Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel); + +Selection lookup_field(gbAllocator a, Type *type_, String field_name, bool is_type) { + return lookup_field_with_selection(a, type_, field_name, is_type, empty_selection); +} + +Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) { + GB_ASSERT(type_ != NULL); + + if (str_eq(field_name, str_lit("_"))) { + return empty_selection; + } + + Type *type = type_deref(type_); + bool is_ptr = type != type_; + sel.indirect = sel.indirect || is_ptr; + + type = base_type(type); + + if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + String type_info_str = str_lit("type_info"); + String data_str = str_lit("data"); + if (entity__any_type_info == NULL) { + entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0); + } + if (entity__any_data == NULL) { + entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1); + } + + if (str_eq(field_name, type_info_str)) { + selection_add_index(&sel, 0); + sel.entity = entity__any_type_info; + return sel; + } else if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 1); + sel.entity = entity__any_data; + return sel; + } + } break; + case Basic_string: { + String data_str = str_lit("data"); + String count_str = str_lit("count"); + if (entity__string_data == NULL) { + entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0); + } + + if (entity__string_count == NULL) { + entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1); + } + + if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 0); + sel.entity = entity__string_data; + return sel; + } else if (str_eq(field_name, count_str)) { + selection_add_index(&sel, 1); + sel.entity = entity__string_count; + return sel; + } + } break; + } + + return sel; + } else if (type->kind == Type_Array) { + String count_str = str_lit("count"); + // NOTE(bill): Underlying memory address cannot be changed + if (str_eq(field_name, count_str)) { + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count)); + return sel; + } + } else if (type->kind == Type_Vector) { + String count_str = str_lit("count"); + // NOTE(bill): Vectors are not addressable + if (str_eq(field_name, count_str)) { + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count)); + return sel; + } + + if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) { + // HACK(bill): Memory leak + switch (type->Vector.count) { + #define _VECTOR_FIELD_CASE(_length, _name) \ + case (_length): \ + if (str_eq(field_name, str_lit(_name))) { \ + selection_add_index(&sel, (_length)-1); \ + sel.entity = make_entity_vector_elem(a, NULL, make_token_ident(str_lit(_name)), type->Vector.elem, (_length)-1); \ + return sel; \ + } \ + /*fallthrough*/ + + _VECTOR_FIELD_CASE(4, "w"); + _VECTOR_FIELD_CASE(3, "z"); + _VECTOR_FIELD_CASE(2, "y"); + _VECTOR_FIELD_CASE(1, "x"); + default: break; + + #undef _VECTOR_FIELD_CASE + } + } + + } else if (type->kind == Type_Slice) { + String data_str = str_lit("data"); + String count_str = str_lit("count"); + String capacity_str = str_lit("capacity"); + + if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 0); + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0); + return sel; + } else if (str_eq(field_name, count_str)) { + selection_add_index(&sel, 1); + if (entity__slice_count == NULL) { + entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1); + } + + sel.entity = entity__slice_count; + return sel; + } else if (str_eq(field_name, capacity_str)) { + selection_add_index(&sel, 2); + if (entity__slice_capacity == NULL) { + entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2); + } + + sel.entity = entity__slice_capacity; + return sel; + } + } + + if (type->kind != Type_Record) { + return sel; + } + if (is_type) { + if (is_type_union(type)) { + // NOTE(bill): The subtype for a union are stored in the fields + // as they are "kind of" like variables but not + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + String str = f->token.string; + + if (str_eq(field_name, str)) { + sel.entity = f; + selection_add_index(&sel, i); + return sel; + } + } + } + + for (isize i = 0; i < type->Record.other_field_count; i++) { + Entity *f = type->Record.other_fields[i]; + GB_ASSERT(f->kind != Entity_Variable); + String str = f->token.string; + + if (str_eq(field_name, str)) { + sel.entity = f; + selection_add_index(&sel, i); + return sel; + } + } + + if (is_type_enum(type)) { + if (str_eq(field_name, str_lit("count"))) { + sel.entity = type->Record.enum_count; + return sel; + } else if (str_eq(field_name, str_lit("min_value"))) { + sel.entity = type->Record.min_value; + return sel; + } else if (str_eq(field_name, str_lit("max_value"))) { + sel.entity = type->Record.max_value; + return sel; + } + } + + } else if (!is_type_enum(type) && !is_type_union(type)) { + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + String str = f->token.string; + if (str_eq(field_name, str)) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + return sel; + } + + if (f->flags & EntityFlag_Anonymous) { + isize prev_count = sel.index.count; + selection_add_index(&sel, i); // HACK(bill): Leaky memory + + sel = lookup_field_with_selection(a, f->type, field_name, is_type, sel); + + if (sel.entity != NULL) { + if (is_type_pointer(f->type)) { + sel.indirect = true; + } + return sel; + } + sel.index.count = prev_count; + } + } + } + + return sel; +} + + + +i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t); +i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t); +i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index); + +i64 align_formula(i64 size, i64 align) { + if (align > 0) { + i64 result = size + align-1; + return result - result%align; + } + return size; +} + +i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + + switch (t->kind) { + case Type_Array: + return type_align_of(s, allocator, t->Array.elem); + case Type_Vector: { + i64 size = type_size_of(s, allocator, t->Vector.elem); + i64 count = gb_max(prev_pow2(t->Vector.count), 1); + i64 total = size * count; + return gb_clamp(total, 1, s.max_align); + } break; + + case Type_Tuple: { + i64 max = 1; + for (isize i = 0; i < t->Tuple.variable_count; i++) { + i64 align = type_align_of(s, allocator, t->Tuple.variables[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + + case Type_Maybe: + return gb_max(type_align_of(s, allocator, t->Maybe.elem), type_align_of(s, allocator, t_bool)); + + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (t->Record.field_count > 0) { + // TODO(bill): What is this supposed to be? + if (t->Record.struct_is_packed) { + i64 max = s.word_size; + for (isize i = 1; i < t->Record.field_count; i++) { + // NOTE(bill): field zero is null + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } + return type_align_of(s, allocator, t->Record.fields[0]->type); + } + break; + case TypeRecord_Union: { + i64 max = 1; + for (isize i = 1; i < t->Record.field_count; i++) { + // NOTE(bill): field zero is null + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + case TypeRecord_RawUnion: { + i64 max = 1; + for (isize i = 0; i < t->Record.field_count; i++) { + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + case TypeRecord_Enum: + return type_align_of(s, allocator, t->Record.enum_base); + } + } break; + } + + // return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align); + // NOTE(bill): Things that are bigger than s.word_size, are actually comprised of smaller types + // TODO(bill): Is this correct for 128-bit types (integers)? + return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.word_size); +} + +i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count, bool is_packed) { + i64 *offsets = gb_alloc_array(allocator, i64, field_count); + i64 curr_offset = 0; + if (is_packed) { + for (isize i = 0; i < field_count; i++) { + offsets[i] = curr_offset; + curr_offset += type_size_of(s, allocator, fields[i]->type); + } + + } else { + for (isize i = 0; i < field_count; i++) { + i64 align = type_align_of(s, allocator, fields[i]->type); + curr_offset = align_formula(curr_offset, align); + offsets[i] = curr_offset; + curr_offset += type_size_of(s, allocator, fields[i]->type); + } + } + return offsets; +} + +bool type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + if (is_type_struct(t)) { + if (!t->Record.struct_are_offsets_set) { + t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed); + t->Record.struct_are_offsets_set = true; + return true; + } + } else if (is_type_tuple(t)) { + if (!t->Tuple.are_offsets_set) { + t->Tuple.offsets = type_set_offsets_of(s, allocator, t->Tuple.variables, t->Tuple.variable_count, false); + t->Tuple.are_offsets_set = true; + return true; + } + } else { + GB_PANIC("Invalid type for setting offsets"); + } + return false; +} + +i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Basic: { + GB_ASSERT(is_type_typed(t)); + BasicKind kind = t->Basic.kind; + i64 size = t->Basic.size; + if (size > 0) { + return size; + } + switch (kind) { + case Basic_string: return 2*s.word_size; + case Basic_any: return 2*s.word_size; + + case Basic_int: case Basic_uint: case Basic_rawptr: + return s.word_size; + } + } break; + + case Type_Array: { + i64 count = t->Array.count; + 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); + return alignment*(count-1) + size; + } break; + + case Type_Vector: { + i64 count = t->Vector.count; + 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)) { + bit_size = 1; // NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1` + // Silly LLVM spec + } + i64 total_size_in_bits = bit_size * count; + i64 total_size = (total_size_in_bits+7)/8; + return total_size; + } break; + + + case Type_Slice: // ptr + len + cap + return 3 * s.word_size; + + case Type_Maybe: { // value + bool + Type *elem = t->Maybe.elem; + i64 align = type_align_of(s, allocator, elem); + i64 size = align_formula(type_size_of(s, allocator, elem), align); + size += type_size_of(s, allocator, t_bool); + return align_formula(size, align); + } + + case Type_Tuple: { + i64 count = t->Tuple.variable_count; + if (count == 0) { + return 0; + } + type_set_offsets(s, allocator, t); + i64 size = t->Tuple.offsets[count-1] + type_size_of(s, allocator, t->Tuple.variables[count-1]->type); + i64 align = type_align_of(s, allocator, t); + return align_formula(size, align); + } break; + + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + i64 count = t->Record.field_count; + if (count == 0) { + return 0; + } + type_set_offsets(s, allocator, t); + i64 size = t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type); + i64 align = type_align_of(s, allocator, t); + return align_formula(size, align); + } break; + + case TypeRecord_Union: { + i64 count = t->Record.field_count; + i64 max = 0; + // NOTE(bill): Zeroth field is invalid + for (isize i = 1; i < count; i++) { + i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); + if (max < size) { + max = size; + } + } + // NOTE(bill): Align to int + i64 align = type_align_of(s, allocator, t); + isize size = align_formula(max, s.word_size); + size += type_size_of(s, allocator, t_int); + return align_formula(size, align); + } break; + + case TypeRecord_RawUnion: { + i64 count = t->Record.field_count; + 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) { + max = size; + } + } + // TODO(bill): Is this how it should work? + i64 align = type_align_of(s, allocator, t); + return align_formula(max, align); + } break; + + case TypeRecord_Enum: { + return type_size_of(s, allocator, t->Record.enum_base); + } break; + } + } break; + } + + // Catch all + return s.word_size; +} + +i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) { + t = base_type(t); + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->Record.field_count-1)) { + return t->Record.struct_offsets[index]; + } + } else if (t->kind == Type_Tuple) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->Tuple.variable_count-1)) { + return t->Tuple.offsets[index]; + } + } else if (t->kind == Type_Basic) { + if (t->Basic.kind == Basic_string) { + switch (index) { + case 0: return 0; + case 1: return s.word_size; + } + } else if (t->Basic.kind == Basic_any) { + switch (index) { + case 0: return 0; + case 1: return s.word_size; + } + } + } else if (t->kind == Type_Slice) { + switch (index) { + case 0: return 0; + case 1: return 1*s.word_size; + case 2: return 2*s.word_size; + } + } + return 0; +} + + +i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *type, Selection sel) { + GB_ASSERT(sel.indirect == false); + + Type *t = type; + i64 offset = 0; + for_array(i, sel.index) { + isize index = sel.index.e[i]; + t = base_type(t); + offset += type_offset_of(s, allocator, t, index); + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { + t = t->Record.fields[index]->type; + } else { + // NOTE(bill): string/any/slices don't have record fields so this case doesn't need to be handled + } + } + return offset; +} + + + +gbString write_type_to_string(gbString str, Type *type) { + if (type == NULL) { + return gb_string_appendc(str, ""); + } + + switch (type->kind) { + case Type_Basic: + str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len); + break; + + case Type_Pointer: + str = gb_string_appendc(str, "^"); + str = write_type_to_string(str, type->Pointer.elem); + break; + + case Type_Maybe: + str = gb_string_appendc(str, "?"); + str = write_type_to_string(str, type->Maybe.elem); + break; + + case Type_Array: + str = gb_string_appendc(str, gb_bprintf("[%td]", type->Array.count)); + str = write_type_to_string(str, type->Array.elem); + break; + + case Type_Vector: + str = gb_string_appendc(str, gb_bprintf("{%td}", type->Vector.count)); + str = write_type_to_string(str, type->Vector.elem); + break; + + case Type_Slice: + str = gb_string_appendc(str, "[]"); + str = write_type_to_string(str, type->Array.elem); + break; + + case Type_Record: { + switch (type->Record.kind) { + case TypeRecord_Struct: + str = gb_string_appendc(str, "struct"); + if (type->Record.struct_is_packed) { + str = gb_string_appendc(str, " #packed"); + } + if (type->Record.struct_is_ordered) { + str = gb_string_appendc(str, " #ordered"); + } + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, "; "); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_Union: + str = gb_string_appendc(str, "union{"); + for (isize i = 1; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + if (i > 1) { + str = gb_string_appendc(str, "; "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, base_type(f->type)); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_RawUnion: + str = gb_string_appendc(str, "raw_union{"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_Enum: + str = gb_string_appendc(str, "enum "); + str = write_type_to_string(str, type->Record.enum_base); + break; + } + } break; + + + case Type_Named: + if (type->Named.type_name != NULL) { + str = gb_string_append_length(str, type->Named.name.text, type->Named.name.len); + } else { + // NOTE(bill): Just in case + str = gb_string_appendc(str, ""); + } + break; + + case Type_Tuple: + if (type->Tuple.variable_count > 0) { + for (isize i = 0; i < type->Tuple.variable_count; i++) { + Entity *var = type->Tuple.variables[i]; + if (var != NULL) { + GB_ASSERT(var->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, ", "); + str = write_type_to_string(str, var->type); + } + } + } + break; + + case Type_Proc: + str = gb_string_appendc(str, "proc("); + if (type->Proc.params) + str = write_type_to_string(str, type->Proc.params); + str = gb_string_appendc(str, ")"); + if (type->Proc.results) { + str = gb_string_appendc(str, " -> "); + str = write_type_to_string(str, type->Proc.results); + } + break; + } + + return str; +} + + +gbString type_to_string(Type *type) { + gbString str = gb_string_make(gb_heap_allocator(), ""); + return write_type_to_string(str, type); +} + + diff --git a/src/common.c b/src/common.c new file mode 100644 index 000000000..9b70722d1 --- /dev/null +++ b/src/common.c @@ -0,0 +1,195 @@ +#define GB_NO_DEFER +#define GB_IMPLEMENTATION +#include "gb/gb.h" + +gbAllocator heap_allocator(void) { + return gb_heap_allocator(); +} + +#include "string.c" +#include "array.c" + +gb_global String global_module_path = {0}; +gb_global bool global_module_path_set = false; + + +String get_module_dir() { + if (global_module_path_set) { + return global_module_path; + } + + Array(wchar_t) path_buf; + array_init_count(&path_buf, heap_allocator(), 300); + + isize len = 0; + for (;;) { + len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count); + if (len == 0) { + return make_string(NULL, 0); + } + if (len < path_buf.count) { + break; + } + array_resize(&path_buf, 2*path_buf.count + 300); + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1); + + GetModuleFileNameW(NULL, text, len); + String path = string16_to_string(heap_allocator(), make_string16(text, len)); + for (isize i = path.len-1; i >= 0; i--) { + u8 c = path.text[i]; + if (c == '/' || c == '\\') { + break; + } + path.len--; + } + + global_module_path = path; + global_module_path_set = true; + + gb_temp_arena_memory_end(tmp); + + array_free(&path_buf); + + return path; +} + +String path_to_fullpath(gbAllocator a, String s) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + String16 string16 = string_to_string16(string_buffer_allocator, s); + String result = {0}; + + DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL); + if (len != 0) { + wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1); + GetFullPathNameW(string16.text, len, text, NULL); + text[len] = 0; + result = string16_to_string(a, make_string16(text, len)); + } + gb_temp_arena_memory_end(tmp); + return result; +} + +i64 next_pow2(i64 n) { + if (n <= 0) { + return 0; + } + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n++; + return n; +} + +i64 prev_pow2(i64 n) { + if (n <= 0) { + return 0; + } + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + return n - (n >> 1); +} + +i16 f32_to_f16(f32 value) { + union { u32 i; f32 f; } v; + i32 i, s, e, m; + + v.f = value; + i = (i32)v.i; + + s = (i >> 16) & 0x00008000; + e = ((i >> 23) & 0x000000ff) - (127 - 15); + m = i & 0x007fffff; + + + if (e <= 0) { + if (e < -10) return cast(i16)s; + m = (m | 0x00800000) >> (1 - e); + + if (m & 0x00001000) + m += 0x00002000; + + return cast(i16)(s | (m >> 13)); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */ + } else { + /* NOTE(bill): NAN */ + m >>= 13; + return cast(i16)(s | 0x7c00 | m | (m == 0)); + } + } else { + if (m & 0x00001000) { + m += 0x00002000; + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + float volatile f = 1e12f; + int j; + for (j = 0; j < 10; j++) + f *= f; /* NOTE(bill): Cause overflow */ + + return cast(i16)(s | 0x7c00); + } + + return cast(i16)(s | (e << 10) | (m >> 13)); + } +} + + + +#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) + + +// Doubly Linked Lists + +#define DLIST_SET(curr_element, next_element) do { \ + (curr_element)->next = (next_element); \ + (curr_element)->next->prev = (curr_element); \ + (curr_element) = (curr_element)->next; \ +} while (0) + +#define DLIST_APPEND(root_element, curr_element, next_element) do { \ + if ((root_element) == NULL) { \ + (root_element) = (curr_element) = (next_element); \ + } else { \ + DLIST_SET(curr_element, next_element); \ + } \ +} while (0) + +//////////////////////////////////////////////////////////////// +// +// Generic Data Structures +// +//////////////////////////////////////////////////////////////// + + +#define MAP_TYPE String +#define MAP_PROC map_string_ +#define MAP_NAME MapString +#include "map.c" + +#define MAP_TYPE bool +#define MAP_PROC map_bool_ +#define MAP_NAME MapBool +#include "map.c" + +#define MAP_TYPE isize +#define MAP_PROC map_isize_ +#define MAP_NAME MapIsize +#include "map.c" diff --git a/src/exact_value.c b/src/exact_value.c new file mode 100644 index 000000000..313cda694 --- /dev/null +++ b/src/exact_value.c @@ -0,0 +1,400 @@ +#include + +// TODO(bill): Big numbers +// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!! + +typedef struct AstNode AstNode; + +typedef enum ExactValueKind { + ExactValue_Invalid, + + ExactValue_Bool, + ExactValue_String, + ExactValue_Integer, + ExactValue_Float, + ExactValue_Pointer, + ExactValue_Compound, // TODO(bill): Is this good enough? + + ExactValue_Count, +} ExactValueKind; + +typedef struct ExactValue { + ExactValueKind kind; + union { + bool value_bool; + String value_string; + i64 value_integer; // NOTE(bill): This must be an integer and not a pointer + f64 value_float; + i64 value_pointer; + AstNode *value_compound; + }; +} ExactValue; + +HashKey hash_exact_value(ExactValue v) { + return hashing_proc(&v, gb_size_of(ExactValue)); +} + + +ExactValue make_exact_value_compound(AstNode *node) { + ExactValue result = {ExactValue_Compound}; + result.value_compound = node; + return result; +} + +ExactValue make_exact_value_bool(bool b) { + ExactValue result = {ExactValue_Bool}; + result.value_bool = (b != 0); + return result; +} + +ExactValue make_exact_value_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_String}; + result.value_string = string; + return result; +} + +ExactValue make_exact_value_integer_from_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_Integer}; + i32 base = 10; + if (string.text[0] == '0') { + switch (string.text[1]) { + case 'b': base = 2; break; + case 'o': base = 8; break; + case 'd': base = 10; break; + case 'x': base = 16; break; + } + } + + result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base); + + return result; +} + +ExactValue make_exact_value_integer(i64 i) { + ExactValue result = {ExactValue_Integer}; + result.value_integer = i; + return result; +} + +ExactValue make_exact_value_float_from_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_Float}; + result.value_float = gb_str_to_f64(cast(char *)string.text, NULL); + return result; +} + +ExactValue make_exact_value_float(f64 f) { + ExactValue result = {ExactValue_Float}; + result.value_float = f; + return result; +} + +ExactValue make_exact_value_pointer(i64 ptr) { + ExactValue result = {ExactValue_Pointer}; + result.value_pointer = ptr; + return result; +} + +ExactValue make_exact_value_from_basic_literal(Token token) { + switch (token.kind) { + case Token_String: return make_exact_value_string(token.string); + case Token_Integer: return make_exact_value_integer_from_string(token.string); + case Token_Float: return make_exact_value_float_from_string(token.string); + case Token_Rune: { + Rune r = GB_RUNE_INVALID; + gb_utf8_decode(token.string.text, token.string.len, &r); + // gb_printf("%.*s rune: %d\n", LIT(token.string), r); + return make_exact_value_integer(r); + } + default: + GB_PANIC("Invalid token for basic literal"); + break; + } + + ExactValue result = {ExactValue_Invalid}; + return result; +} + +ExactValue exact_value_to_integer(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return v; + case ExactValue_Float: { + i64 i = cast(i64)v.value_float; + f64 f = cast(f64)i; + if (f == v.value_float) { + return make_exact_value_integer(i); + } + } break; + + case ExactValue_Pointer: + return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_to_float(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return make_exact_value_float(cast(i64)v.value_integer); + case ExactValue_Float: + return v; + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + + +ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) { + switch (op.kind) { + case Token_Add: { + switch (v.kind) { + case ExactValue_Invalid: + case ExactValue_Integer: + case ExactValue_Float: + return v; + } + } break; + + case Token_Sub: { + switch (v.kind) { + case ExactValue_Invalid: + return v; + case ExactValue_Integer: { + ExactValue i = v; + i.value_integer = -i.value_integer; + return i; + } + case ExactValue_Float: { + ExactValue i = v; + i.value_float = -i.value_float; + return i; + } + } + } break; + + case Token_Xor: { + i64 i = 0; + switch (v.kind) { + case ExactValue_Invalid: + return v; + case ExactValue_Integer: + i = v.value_integer; + i = ~i; + break; + default: + goto failure; + } + + // NOTE(bill): unsigned integers will be negative and will need to be + // limited to the types precision + if (precision > 0) + i &= ~((~0ll)<kind) { + case ExactValue_Invalid: + *y = *x; + return; + + case ExactValue_Bool: + case ExactValue_String: + return; + + case ExactValue_Integer: + switch (y->kind) { + case ExactValue_Integer: + return; + case ExactValue_Float: + // TODO(bill): Is this good enough? + *x = make_exact_value_float(cast(f64)x->value_integer); + return; + } + break; + + case ExactValue_Float: + if (y->kind == ExactValue_Float) + return; + break; + } + + compiler_error("How'd you get here? Invalid ExactValueKind"); +} + +// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough? +ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) { + match_exact_values(&x, &y); + + switch (x.kind) { + case ExactValue_Invalid: + return x; + + case ExactValue_Bool: + switch (op.kind) { + case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool); + case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool); + case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool); + case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool); + default: goto error; + } + break; + + case ExactValue_Integer: { + i64 a = x.value_integer; + i64 b = y.value_integer; + i64 c = 0; + switch (op.kind) { + case Token_Add: c = a + b; break; + case Token_Sub: c = a - b; break; + case Token_Mul: c = a * b; break; + case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b)); + case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division + case Token_Mod: c = a % b; break; + case Token_And: c = a & b; break; + case Token_Or: c = a | b; break; + case Token_Xor: c = a ^ b; break; + case Token_AndNot: c = a&(~b); break; + case Token_Shl: c = a << b; break; + case Token_Shr: c = a >> b; break; + default: goto error; + } + return make_exact_value_integer(c); + } break; + + case ExactValue_Float: { + f64 a = x.value_float; + f64 b = y.value_float; + switch (op.kind) { + case Token_Add: return make_exact_value_float(a + b); + case Token_Sub: return make_exact_value_float(a - b); + case Token_Mul: return make_exact_value_float(a * b); + case Token_Quo: return make_exact_value_float(a / b); + default: goto error; + } + } break; + } + +error: + ExactValue error_value = {0}; + // gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind)); + return error_value; +} + +gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); } + + +i32 cmp_f64(f64 a, f64 b) { + return (a > b) - (a < b); +} + +bool compare_exact_values(Token op, ExactValue x, ExactValue y) { + match_exact_values(&x, &y); + + switch (x.kind) { + case ExactValue_Invalid: + return false; + + case ExactValue_Bool: + switch (op.kind) { + case Token_CmpEq: return x.value_bool == y.value_bool; + case Token_NotEq: return x.value_bool != y.value_bool; + } + break; + + case ExactValue_Integer: { + i64 a = x.value_integer; + i64 b = y.value_integer; + switch (op.kind) { + case Token_CmpEq: return a == b; + case Token_NotEq: return a != b; + case Token_Lt: return a < b; + case Token_LtEq: return a <= b; + case Token_Gt: return a > b; + case Token_GtEq: return a >= b; + } + } break; + + case ExactValue_Float: { + f64 a = x.value_float; + f64 b = y.value_float; + switch (op.kind) { + case Token_CmpEq: return cmp_f64(a, b) == 0; + case Token_NotEq: return cmp_f64(a, b) != 0; + case Token_Lt: return cmp_f64(a, b) < 0; + case Token_LtEq: return cmp_f64(a, b) <= 0; + case Token_Gt: return cmp_f64(a, b) > 0; + case Token_GtEq: return cmp_f64(a, b) >= 0; + } + } break; + + case ExactValue_String: { + String a = x.value_string; + String b = y.value_string; + isize len = gb_min(a.len, b.len); + // TODO(bill): gb_memcompare is used because the strings are UTF-8 + switch (op.kind) { + case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0; + case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0; + case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0; + case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0; + case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0; + case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0; + } + } break; + } + + GB_PANIC("Invalid comparison"); + return false; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..cf5d2379b --- /dev/null +++ b/src/main.c @@ -0,0 +1,272 @@ +#if defined(__cplusplus) +extern "C" { +#endif +#define VERSION_STRING "v0.0.3c" + +#include "common.c" +#include "timings.c" +#include "unicode.c" +#include "tokenizer.c" +#include "parser.c" +// #include "printer.c" +#include "checker/checker.c" +#include "ssa.c" +#include "ssa_opt.c" +#include "ssa_print.c" +// #include "vm.c" + +// NOTE(bill): `name` is used in debugging and profiling modes +i32 win32_exec_command_line_app(char *name, char *fmt, ...) { + STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)}; + PROCESS_INFORMATION pi = {0}; + char cmd_line[4096] = {0}; + isize cmd_len; + va_list va; + gbTempArenaMemory tmp; + String16 cmd; + i32 exit_code = 0; + + start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_SHOW; + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + va_start(va, fmt); + cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va); + va_end(va); + // gb_printf("%.*s\n", cast(int)cmd_len, cmd_line); + + tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1)); + + if (CreateProcessW(NULL, cmd.text, + NULL, NULL, true, 0, NULL, NULL, + &start_info, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } else { + // NOTE(bill): failed to create process + gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line); + exit_code = -1; + } + + gb_temp_arena_memory_end(tmp); + return exit_code; +} + +typedef enum ArchKind { + ArchKind_x64, + ArchKind_x86, +} ArchKind; + +typedef struct ArchData { + BaseTypeSizes sizes; + String llc_flags; + String link_flags; +} ArchData; + +ArchData make_arch_data(ArchKind kind) { + ArchData data = {0}; + + switch (kind) { + case ArchKind_x64: + default: + data.sizes.word_size = 8; + data.sizes.max_align = 16; + data.llc_flags = str_lit("-march=x86-64 "); + data.link_flags = str_lit("/machine:x64 "); + break; + + case ArchKind_x86: + data.sizes.word_size = 4; + data.sizes.max_align = 8; + data.llc_flags = str_lit("-march=x86 "); + data.link_flags = str_lit("/machine:x86 "); + break; + } + + 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) { + usage(argv[0]); + return 1; + } + + Timings timings = {0}; + timings_init(&timings, str_lit("Total Time"), 128); + // defer (timings_destroy(&timings)); + +#if 1 + init_string_buffer_memory(); + init_global_error_collector(); + + String module_dir = get_module_dir(); + + init_universal_scope(); + + char *init_filename = NULL; + bool run_output = false; + String arg1 = make_string_c(argv[1]); + if (str_eq(arg1, str_lit("run"))) { + run_output = true; + init_filename = argv[2]; + } else if (str_eq(arg1, str_lit("build"))) { + init_filename = argv[2]; + } else if (str_eq(arg1, str_lit("version"))) { + gb_printf("%s version %s", argv[0], VERSION_STRING); + return 0; + } else { + usage(argv[0]); + return 1; + } + + // TODO(bill): prevent compiling without a linker + + timings_start_section(&timings, str_lit("parse files")); + + Parser parser = {0}; + if (!init_parser(&parser)) { + return 1; + } + // defer (destroy_parser(&parser)); + + if (parse_files(&parser, init_filename) != ParseFile_None) { + return 1; + } + + +#if 1 + timings_start_section(&timings, str_lit("type check")); + + Checker checker = {0}; + ArchData arch_data = make_arch_data(ArchKind_x64); + + init_checker(&checker, &parser, arch_data.sizes); + // defer (destroy_checker(&checker)); + + check_parsed_files(&checker); + + +#endif +#if 1 + + ssaGen ssa = {0}; + if (!ssa_gen_init(&ssa, &checker)) { + return 1; + } + // defer (ssa_gen_destroy(&ssa)); + + timings_start_section(&timings, str_lit("ssa gen")); + ssa_gen_tree(&ssa); + + timings_start_section(&timings, str_lit("ssa opt")); + ssa_opt_tree(&ssa); + + timings_start_section(&timings, str_lit("ssa print")); + ssa_print_llvm_ir(&ssa); + + // prof_print_all(); + +#if 1 + timings_start_section(&timings, str_lit("llvm-opt")); + + char const *output_name = ssa.output_file.filename; + isize base_name_len = gb_path_extension(output_name)-1 - output_name; + String output = make_string(cast(u8 *)output_name, base_name_len); + + i32 optimization_level = 0; + optimization_level = gb_clamp(optimization_level, 0, 3); + + i32 exit_code = 0; + // For more passes arguments: http://llvm.org/docs/Passes.html + exit_code = win32_exec_command_line_app("llvm-opt", + "%.*sbin/opt %s -o %.*s.bc " + "-mem2reg " + "-memcpyopt " + "-die " + // "-dse " + // "-dce " + // "-S " + "", + LIT(module_dir), + output_name, LIT(output)); + if (exit_code != 0) { + return exit_code; + } + + #if 1 + timings_start_section(&timings, str_lit("llvm-llc")); + // For more arguments: http://llvm.org/docs/CommandGuide/llc.html + exit_code = win32_exec_command_line_app("llvm-llc", + "%.*sbin/llc %.*s.bc -filetype=obj -O%d " + "%.*s " + // "-debug-pass=Arguments " + "", + LIT(module_dir), + LIT(output), + optimization_level, + LIT(arch_data.llc_flags)); + if (exit_code != 0) { + return exit_code; + } + + timings_start_section(&timings, str_lit("msvc-link")); + + gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib"); + // defer (gb_string_free(lib_str)); + char lib_str_buf[1024] = {0}; + for_array(i, parser.foreign_libraries) { + String lib = parser.foreign_libraries.e[i]; + isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), + " %.*s.lib", LIT(lib)); + lib_str = gb_string_appendc(lib_str, lib_str_buf); + } + + exit_code = win32_exec_command_line_app("msvc-link", + "link %.*s.obj -OUT:%.*s.exe %s " + "/defaultlib:libcmt " + "/nologo /incremental:no /opt:ref /subsystem:console " + " %.*s " + "", + LIT(output), LIT(output), + lib_str, LIT(arch_data.link_flags)); + if (exit_code != 0) { + return exit_code; + } + + // timings_print_all(&timings); + + if (run_output) { + win32_exec_command_line_app("odin run", + "%.*s.exe", cast(int)base_name_len, output_name); + } + #endif +#endif +#endif +#endif + + + return 0; +} + +#if defined(__cplusplus) +} +#endif diff --git a/src/old_vm.c b/src/old_vm.c new file mode 100644 index 000000000..071af7ae3 --- /dev/null +++ b/src/old_vm.c @@ -0,0 +1,1305 @@ +// TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER +#include "dyncall/include/dyncall.h" + +struct VirtualMachine; + +struct vmValueProc { + ssaProcedure *proc; // If `NULL`, use `ptr` instead and call external procedure + void * ptr; +}; + + +struct vmValue { + // NOTE(bill): Shouldn't need to store type here as the type checking + // has already been handled in the SSA + union { + f32 val_f32; + f64 val_f64; + void * val_ptr; + i64 val_int; + vmValueProc val_proc; + }; + Array val_comp; // NOTE(bill): Will be freed through "stack" + Type *type; +}; + +vmValue vm_make_value_ptr(Type *type, void *ptr) { + GB_ASSERT(is_type_pointer(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_ptr = ptr; + return v; +} +vmValue vm_make_value_int(Type *type, i64 i) { + GB_ASSERT(is_type_integer(type) || + is_type_boolean(type) || + is_type_enum(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_int = i; + return v; +} +vmValue vm_make_value_f32(Type *type, f32 f) { + GB_ASSERT(is_type_f32(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_f32 = f; + return v; +} +vmValue vm_make_value_f64(Type *type, f64 f) { + GB_ASSERT(is_type_f64(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_f64 = f; + return v; +} +vmValue vm_make_value_comp(Type *type, gbAllocator allocator, isize count) { + GB_ASSERT(is_type_string(type) || + is_type_any (type) || + is_type_array (type) || + is_type_vector(type) || + is_type_slice (type) || + is_type_maybe (type) || + is_type_struct(type) || + is_type_union(type) || + is_type_raw_union(type) || + is_type_tuple (type) || + is_type_proc (type)); + vmValue v = {0}; + v.type = default_type(type); + array_init_count(&v.val_comp, allocator, count); + return v; +} + + + + + + +struct vmFrame { + VirtualMachine * vm; + vmFrame * caller; + ssaProcedure * curr_proc; + ssaBlock * prev_block; + ssaBlock * curr_block; + i32 instr_index; // For the current block + + Map values; // Key: ssaValue * + gbTempArenaMemory temp_arena_memory; + gbAllocator stack_allocator; + Array locals; // Memory to locals + vmValue result; +}; + +struct VirtualMachine { + ssaModule * module; + gbArena stack_arena; + gbAllocator stack_allocator; + gbAllocator heap_allocator; + Array frame_stack; + Map globals; // Key: ssaValue * + Map const_compound_lits; // Key: ssaValue * + vmValue exit_value; +}; + +void vm_exec_instr (VirtualMachine *vm, ssaValue *value); +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value); +void vm_store (VirtualMachine *vm, void *dst, vmValue val, Type *type); +vmValue vm_load (VirtualMachine *vm, void *ptr, Type *type); +void vm_print_value (vmValue value, Type *type); + +void vm_jump_block(vmFrame *f, ssaBlock *target) { + f->prev_block = f->curr_block; + f->curr_block = target; + f->instr_index = 0; +} + + +vmFrame *vm_back_frame(VirtualMachine *vm) { + if (vm->frame_stack.count > 0) { + return &vm->frame_stack[vm->frame_stack.count-1]; + } + return NULL; +} + +i64 vm_type_size_of(VirtualMachine *vm, Type *type) { + return type_size_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_align_of(VirtualMachine *vm, Type *type) { + return type_align_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_offset_of(VirtualMachine *vm, Type *type, i64 index) { + return type_offset_of(vm->module->sizes, vm->heap_allocator, type, index); +} + + +void vm_init(VirtualMachine *vm, ssaModule *module) { + gb_arena_init_from_allocator(&vm->stack_arena, heap_allocator(), gb_megabytes(64)); + + vm->module = module; + vm->stack_allocator = gb_arena_allocator(&vm->stack_arena); + vm->heap_allocator = heap_allocator(); + array_init(&vm->frame_stack, vm->heap_allocator); + map_init(&vm->globals, vm->heap_allocator); + map_init(&vm->const_compound_lits, vm->heap_allocator); + + for_array(i, vm->module->values.entries) { + ssaValue *v = vm->module->values.entries[i].value; + switch (v->kind) { + case ssaValue_Global: { + Type *t = ssa_type(v); + GB_ASSERT(is_type_pointer(t)); + i64 size = vm_type_size_of(vm, t); + i64 align = vm_type_align_of(vm, t); + void *mem = gb_alloc_align(vm->heap_allocator, size, align); + if (v->Global.value != NULL && v->Global.value->kind == ssaValue_Constant) { + vm_store(vm, mem, vm_operand_value(vm, v->Global.value), type_deref(t)); + } + map_set(&vm->globals, hash_pointer(v), vm_make_value_ptr(t, mem)); + } break; + } + } + +} +void vm_destroy(VirtualMachine *vm) { + array_free(&vm->frame_stack); + map_destroy(&vm->globals); + map_destroy(&vm->const_compound_lits); + gb_arena_free(&vm->stack_arena); +} + + + + + + +void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { + if (v != NULL) { + GB_ASSERT(ssa_type(v) != NULL); + map_set(&f->values, hash_pointer(v), val); + } +} + + + +vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) { + vmFrame frame = {0}; + + frame.vm = vm; + frame.curr_proc = proc; + frame.prev_block = proc->blocks[0]; + frame.curr_block = proc->blocks[0]; + frame.instr_index = 0; + frame.caller = vm_back_frame(vm); + frame.stack_allocator = vm->stack_allocator; + frame.temp_arena_memory = gb_temp_arena_memory_begin(&vm->stack_arena); + + map_init(&frame.values, vm->heap_allocator); + array_init(&frame.locals, vm->heap_allocator, proc->local_count); + array_add(&vm->frame_stack, frame); + return vm_back_frame(vm); +} + +void vm_pop_frame(VirtualMachine *vm) { + vmFrame *f = vm_back_frame(vm); + + gb_temp_arena_memory_end(f->temp_arena_memory); + array_free(&f->locals); + map_destroy(&f->values); + + array_pop(&vm->frame_stack); +} + + +vmValue vm_call_proc(VirtualMachine *vm, ssaProcedure *proc, Array values) { + Type *type = base_type(proc->type); + GB_ASSERT_MSG(type->Proc.param_count == values.count, + "Incorrect number of arguments passed into procedure call!\n" + "%.*s -> %td vs %td", + LIT(proc->name), + type->Proc.param_count, values.count); + Type *result_type = type->Proc.results; + if (result_type != NULL && + result_type->Tuple.variable_count == 1) { + result_type = result_type->Tuple.variables[0]->type; + } + + if (proc->body == NULL) { + // GB_PANIC("TODO(bill): external procedure"); + gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name)); + vmValue result = {0}; + result.type = result_type; + return result; + } + + void *result_mem = NULL; + if (result_type != NULL) { + result_mem = gb_alloc_align(vm->stack_allocator, + vm_type_size_of(vm, result_type), + vm_type_align_of(vm, result_type)); + } + + gb_printf("call: %.*s\n", LIT(proc->name)); + + vmFrame *f = vm_push_frame(vm, proc); + for_array(i, proc->params) { + vm_set_value(f, proc->params[i], values[i]); + } + + while (f->curr_block != NULL) { + ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++]; + vm_exec_instr(vm, curr_instr); + } + + + + + if (type->Proc.result_count > 0) { + vmValue r = f->result; + + gb_printf("%.*s -> ", LIT(proc->name)); + vm_print_value(r, result_type); + gb_printf("\n"); + + vm_store(vm, result_mem, r, result_type); + } + + vm_pop_frame(vm); + if (result_mem != NULL) { + return vm_load(vm, result_mem, result_type); + } + + vmValue void_result = {0}; + return void_result; +} + + +ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { + ssaValue *v = ssa_lookup_member(vm->module, name); + GB_ASSERT(v->kind == ssaValue_Proc); + return &v->Proc; +} + +vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array args) { + return vm_call_proc(vm, vm_lookup_procedure(vm, name), args); +} + +vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) { + Type *original_type = t; + t = base_type(get_enum_base_type(t)); + // i64 size = vm_type_size_of(vm, t); + if (is_type_boolean(t)) { + return vm_make_value_int(original_type, value.value_bool); + } else if (is_type_integer(t)) { + return vm_make_value_int(original_type, value.value_integer); + } else if (is_type_float(t)) { + if (t->Basic.kind == Basic_f32) { + return vm_make_value_f32(original_type, cast(f32)value.value_float); + } else if (t->Basic.kind == Basic_f64) { + return vm_make_value_f64(original_type, cast(f64)value.value_float); + } + } else if (is_type_pointer(t)) { + return vm_make_value_ptr(original_type, cast(void *)cast(intptr)value.value_pointer); + } else if (is_type_string(t)) { + vmValue result = vm_make_value_comp(original_type, vm->stack_allocator, 2); + + String str = value.value_string; + i64 len = str.len; + u8 *text = gb_alloc_array(vm->heap_allocator, u8, len); + gb_memcopy(text, str.text, len); + + result.val_comp[0] = vm_make_value_ptr(t_u8_ptr, text); + result.val_comp[1] = vm_make_value_int(t_int, len); + + return result; + } else if (value.kind == ExactValue_Compound) { + if (ptr != NULL) { + vmValue *found = map_get(&vm->const_compound_lits, hash_pointer(ptr)); + if (found != NULL) { + return *found; + } + } + + ast_node(cl, CompoundLit, value.value_compound); + + if (is_type_array(t)) { + vmValue result = {0}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_vector(t)) { + vmValue result = {0}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_struct(t)) { + ast_node(cl, CompoundLit, value.value_compound); + + isize value_count = t->Record.field_count; + vmValue result = vm_make_value_comp(t, vm->heap_allocator, value_count); + + if (cl->elems.count == 0) { + return result; + } + + if (cl->elems[0]->kind == AstNode_FieldValue) { + isize elem_count = cl->elems.count; + for (isize i = 0; i < elem_count; i++) { + ast_node(fv, FieldValue, cl->elems[i]); + String name = fv->field->Ident.string; + + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, fv->value); + GB_ASSERT(tav != NULL); + + Selection sel = lookup_field(vm->heap_allocator, t, name, false); + Entity *f = t->Record.fields[sel.index[0]]; + + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } else { + for (isize i = 0; i < value_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + GB_ASSERT(tav != NULL); + Entity *f = t->Record.fields_in_src_order[i]; + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } + + return result; + } else { + GB_PANIC("TODO(bill): Other compound types\n"); + } + + } else if (value.kind == ExactValue_Invalid) { + vmValue zero_result = {0}; + zero_result.type = t; + return zero_result; + } else { + gb_printf_err("TODO(bill): Other constant types: %s\n", type_to_string(original_type)); + } + + GB_ASSERT_MSG(t == NULL, "%s - %d", type_to_string(t), value.kind); + vmValue void_result = {0}; + return void_result; +} + + +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) { + vmFrame *f = vm_back_frame(vm); + vmValue v = {0}; + switch (value->kind) { + case ssaValue_Constant: { + v = vm_exact_value(vm, value, value->Constant.value, value->Constant.type); + } break; + case ssaValue_ConstantSlice: { + ssaValueConstant *cs = &value->ConstantSlice; + v = vm_make_value_comp(ssa_type(value), vm->stack_allocator, 3); + v.val_comp[0] = vm_operand_value(vm, cs->backing_array); + v.val_comp[1] = vm_make_value_int(t_int, cs->count); + v.val_comp[2] = vm_make_value_int(t_int, cs->count); + } break; + case ssaValue_Nil: + GB_PANIC("TODO(bill): ssaValue_Nil"); + break; + case ssaValue_TypeName: + GB_PANIC("ssaValue_TypeName has no operand value"); + break; + case ssaValue_Global: + v = *map_get(&vm->globals, hash_pointer(value)); + break; + case ssaValue_Param: + v = *map_get(&f->values, hash_pointer(value)); + break; + case ssaValue_Proc: { + v.type = ssa_type(value); + v.val_proc.proc = &value->Proc; + // GB_PANIC("TODO(bill): ssaValue_Proc"); + } break; + case ssaValue_Block: + GB_PANIC("ssaValue_Block has no operand value"); + break; + case ssaValue_Instr: { + vmValue *found = map_get(&f->values, hash_pointer(value)); + if (found) { + v = *found; + } else { + GB_PANIC("Invalid instruction"); + } + } break; + } + + return v; +} + +void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + Type *type = val.type; + GB_ASSERT_MSG(is_type_integer(type) || is_type_boolean(type), + "\nExpected integer/boolean, got %s (%s)", + type_to_string(type), + type_to_string(base_type(type))); + i64 size = vm_type_size_of(vm, type); + gb_memcopy(dst, &val.val_int, size); +} + +void vm_store_pointer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + GB_ASSERT(is_type_pointer(val.type)); + gb_memcopy(dst, &val.val_ptr, vm_type_size_of(vm, t_rawptr)); +} + + +void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + // NOTE(bill): enums are pretty much integers + type = base_type(get_enum_base_type(type)); + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + vm_store_integer(vm, dst, val); + break; + case Basic_f32: + *cast(f32 *)dst = val.val_f32; + break; + case Basic_f64: + *cast(f64 *)dst = val.val_f64; + break; + case Basic_rawptr: + vm_store_pointer(vm, dst, val); // NOTE(bill): A pointer can be treated as an integer + break; + case Basic_string: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + } break; + case Basic_any: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_pointer(vm, mem+1*word_size, val.val_comp[1]); + } break; + default: + gb_printf_err("TODO(bill): other basic types for `vm_store` %s\n", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + vm_store_pointer(vm, dst, val); + break; + + case Type_Record: { + u8 *mem = cast(u8 *)dst; + gb_zero_size(mem, size); + + if (is_type_struct(type)) { + GB_ASSERT_MSG(type->Record.field_count >= val.val_comp.count, + "%td vs %td", + type->Record.field_count, val.val_comp.count); + + isize field_count = gb_min(val.val_comp.count, type->Record.field_count); + + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + vm_store(vm, mem+offset, val.val_comp[i], f->type); + } + } else if (is_type_union(type)) { + GB_ASSERT(val.val_comp.count == 2); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + vm_store_integer(vm, mem + size_of_union, val.val_comp[1]); + + } else if (is_type_raw_union(type)) { + GB_ASSERT(val.val_comp.count == 1); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + } else { + GB_PANIC("Unknown record type: %s", type_to_string(type)); + } + } break; + + case Type_Tuple: { + u8 *mem = cast(u8 *)dst; + + GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count, + "%td vs %td", + type->Tuple.variable_count, val.val_comp.count); + + isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count); + + for (isize i = 0; i < variable_count; i++) { + Entity *f = type->Tuple.variables[i]; + void *ptr = mem + vm_type_offset_of(vm, type, i); + vm_store(vm, ptr, val.val_comp[i], f->type); + } + } break; + + case Type_Array: { + Type *elem_type = type->Array.elem; + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Vector: { + Type *elem_type = type->Array.elem; + GB_ASSERT_MSG(!is_type_boolean(elem_type), "TODO(bill): Booleans of vectors"); + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Slice: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + vm_store_integer(vm, mem+2*word_size, val.val_comp[2]); + } break; + + default: + gb_printf_err("TODO(bill): other types for `vm_store` %s\n", type_to_string(type)); + break; + } +} + +vmValue vm_load_integer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {0}; + v.type = type; + GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + +vmValue vm_load_pointer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {0}; + v.type = type; + GB_ASSERT(is_type_pointer(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + + +vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + type = base_type(get_enum_base_type(type)); + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + return vm_load_integer(vm, ptr, original_type); + case Basic_f32: + return vm_make_value_f32(original_type, *cast(f32 *)ptr); + case Basic_f64: + return vm_make_value_f64(original_type, *cast(f64 *)ptr); + case Basic_rawptr: + return vm_load_pointer(vm, ptr, original_type); + + + case Basic_string: { + u8 *mem = cast(u8 *)ptr; + i64 word_size = vm_type_size_of(vm, t_int); + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load_pointer(vm, mem+0*word_size, t_u8_ptr); + result.val_comp[1] = vm_load_integer(vm, mem+1*word_size, t_int); + return result; + } break; + + default: + GB_PANIC("TODO(bill): other basic types for `vm_load` %s", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + return vm_load_pointer(vm, ptr, original_type); + + case Type_Array: { + i64 count = type->Array.count; + Type *elem_type = type->Array.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + i64 offset = elem_size*i; + vmValue val = vm_load(vm, mem+offset, elem_type); + result.val_comp[i] = val; + } + + return result; + } break; + + case Type_Slice: { + Type *elem_type = type->Slice.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 word_size = vm_type_size_of(vm, t_int); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 3); + + u8 *mem = cast(u8 *)ptr; + result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data + result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int); // count + result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int); // capacity + return result; + } break; + + case Type_Record: { + if (is_type_struct(type)) { + isize field_count = type->Record.field_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, field_count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + + return result; + } else if (is_type_union(type)) { + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + u8 *mem = cast(u8 *)ptr; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load(vm, mem, make_type_array(vm->stack_allocator, t_u8, size_of_union)); + result.val_comp[1] = vm_load(vm, mem+size_of_union, t_int); + return result; + } else if (is_type_raw_union(type)) { + gb_printf_err("TODO(bill): load raw_union\n"); + } else { + gb_printf_err("TODO(bill): load other records\n"); + } + } break; + + case Type_Tuple: { + isize count = type->Tuple.variable_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + Entity *f = type->Tuple.variables[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + return result; + } break; + + default: + GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type)); + break; + } + + GB_ASSERT(type == NULL); + vmValue void_result = {0}; + return void_result; +} + +vmValue vm_exec_binary_op(VirtualMachine *vm, Type *type, vmValue lhs, vmValue rhs, TokenKind op) { + vmValue result = {0}; + + type = base_type(type); + if (is_type_vector(type)) { + Type *elem = type->Vector.elem; + i64 count = type->Vector.count; + + result = vm_make_value_comp(type, vm->stack_allocator, count); + + for (i64 i = 0; i < count; i++) { + result.val_comp[i] = vm_exec_binary_op(vm, elem, lhs.val_comp[i], rhs.val_comp[i], op); + } + + return result; + } + + if (gb_is_between(op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_integer(type) || is_type_boolean(type)) { + // TODO(bill): Do I need to take into account the size of the integer? + switch (op) { + case Token_CmpEq: result.val_int = lhs.val_int == rhs.val_int; break; + case Token_NotEq: result.val_int = lhs.val_int != rhs.val_int; break; + case Token_Lt: result.val_int = lhs.val_int < rhs.val_int; break; + case Token_Gt: result.val_int = lhs.val_int > rhs.val_int; break; + case Token_LtEq: result.val_int = lhs.val_int <= rhs.val_int; break; + case Token_GtEq: result.val_int = lhs.val_int >= rhs.val_int; break; + } + } else if (type == t_f32) { + switch (op) { + case Token_CmpEq: result.val_f32 = lhs.val_f32 == rhs.val_f32; break; + case Token_NotEq: result.val_f32 = lhs.val_f32 != rhs.val_f32; break; + case Token_Lt: result.val_f32 = lhs.val_f32 < rhs.val_f32; break; + case Token_Gt: result.val_f32 = lhs.val_f32 > rhs.val_f32; break; + case Token_LtEq: result.val_f32 = lhs.val_f32 <= rhs.val_f32; break; + case Token_GtEq: result.val_f32 = lhs.val_f32 >= rhs.val_f32; break; + } + } else if (type == t_f64) { + switch (op) { + case Token_CmpEq: result.val_f64 = lhs.val_f64 == rhs.val_f64; break; + case Token_NotEq: result.val_f64 = lhs.val_f64 != rhs.val_f64; break; + case Token_Lt: result.val_f64 = lhs.val_f64 < rhs.val_f64; break; + case Token_Gt: result.val_f64 = lhs.val_f64 > rhs.val_f64; break; + case Token_LtEq: result.val_f64 = lhs.val_f64 <= rhs.val_f64; break; + case Token_GtEq: result.val_f64 = lhs.val_f64 >= rhs.val_f64; break; + } + } else if (is_type_string(type)) { + Array args = {0}; + array_init_count(&args, vm->stack_allocator, 2); + args[0] = lhs; + args[1] = rhs; + switch (op) { + case Token_CmpEq: result = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break; + case Token_NotEq: result = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break; + case Token_Lt: result = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break; + case Token_Gt: result = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break; + case Token_LtEq: result = vm_call_proc_by_name(vm, make_string("__string_le"), args); break; + case Token_GtEq: result = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break; + } + } else { + GB_PANIC("TODO(bill): Vector BinaryOp"); + } + } else { + if (is_type_integer(type) || is_type_boolean(type)) { + switch (op) { + case Token_Add: result.val_int = lhs.val_int + rhs.val_int; break; + case Token_Sub: result.val_int = lhs.val_int - rhs.val_int; break; + case Token_And: result.val_int = lhs.val_int & rhs.val_int; break; + case Token_Or: result.val_int = lhs.val_int | rhs.val_int; break; + case Token_Xor: result.val_int = lhs.val_int ^ rhs.val_int; break; + case Token_Shl: result.val_int = lhs.val_int << rhs.val_int; break; + case Token_Shr: result.val_int = lhs.val_int >> rhs.val_int; break; + case Token_Mul: result.val_int = lhs.val_int * rhs.val_int; break; + case Token_Not: result.val_int = lhs.val_int ^ rhs.val_int; break; + + case Token_AndNot: result.val_int = lhs.val_int & (~rhs.val_int); break; + + // TODO(bill): Take into account size of integer and signedness + case Token_Quo: GB_PANIC("TODO(bill): BinaryOp Integer Token_Quo"); break; + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp Integer Token_Mod"); break; + + } + } else if (is_type_float(type)) { + if (type == t_f32) { + switch (op) { + case Token_Add: result.val_f32 = lhs.val_f32 + rhs.val_f32; break; + case Token_Sub: result.val_f32 = lhs.val_f32 - rhs.val_f32; break; + case Token_Mul: result.val_f32 = lhs.val_f32 * rhs.val_f32; break; + case Token_Quo: result.val_f32 = lhs.val_f32 / rhs.val_f32; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f32 Token_Mod"); break; + } + } else if (type == t_f64) { + switch (op) { + case Token_Add: result.val_f64 = lhs.val_f64 + rhs.val_f64; break; + case Token_Sub: result.val_f64 = lhs.val_f64 - rhs.val_f64; break; + case Token_Mul: result.val_f64 = lhs.val_f64 * rhs.val_f64; break; + case Token_Quo: result.val_f64 = lhs.val_f64 / rhs.val_f64; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f64 Token_Mod"); break; + } + } + } else { + GB_PANIC("Invalid binary op type"); + } + } + + return result; +} + +void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { + GB_ASSERT(value != NULL); + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + vmFrame *f = vm_back_frame(vm); + +#if 0 + if (instr->kind != ssaInstr_Comment) { + gb_printf("exec_instr: %.*s\n", LIT(ssa_instr_strings[instr->kind])); + } +#endif + + switch (instr->kind) { + case ssaInstr_StartupRuntime: { +#if 1 + Array args = {0}; // Empty + vm_call_proc_by_name(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME), args); // NOTE(bill): No return value +#endif + } break; + + case ssaInstr_Comment: + break; + + case ssaInstr_Local: { + Type *type = ssa_type(value); + GB_ASSERT(is_type_pointer(type)); + isize size = gb_max(1, vm_type_size_of(vm, type)); + isize align = gb_max(1, vm_type_align_of(vm, type)); + void *memory = gb_alloc_align(vm->stack_allocator, size, align); + GB_ASSERT(memory != NULL); + vm_set_value(f, value, vm_make_value_ptr(type, memory)); + array_add(&f->locals, memory); + } break; + + case ssaInstr_ZeroInit: { + Type *t = type_deref(ssa_type(instr->ZeroInit.address)); + vmValue addr = vm_operand_value(vm, instr->ZeroInit.address); + void *data = addr.val_ptr; + i64 size = vm_type_size_of(vm, t); + gb_zero_size(data, size); + } break; + + case ssaInstr_Store: { + vmValue addr = vm_operand_value(vm, instr->Store.address); + vmValue val = vm_operand_value(vm, instr->Store.value); + GB_ASSERT(val.type != NULL); + Type *t = type_deref(ssa_type(instr->Store.address)); + vm_store(vm, addr.val_ptr, val, t); + } break; + + case ssaInstr_Load: { + vmValue addr = vm_operand_value(vm, instr->Load.address); + Type *t = ssa_type(value); + vmValue v = vm_load(vm, addr.val_ptr, t); + vm_set_value(f, value, v); + } break; + + case ssaInstr_ArrayElementPtr: { + vmValue address = vm_operand_value(vm, instr->ArrayElementPtr.address); + vmValue elem_index = vm_operand_value(vm, instr->ArrayElementPtr.elem_index); + + Type *t = ssa_type(instr->ArrayElementPtr.address); + GB_ASSERT(is_type_pointer(t)); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + void *ptr = cast(u8 *)address.val_ptr + elem_index.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_StructElementPtr: { + vmValue address = vm_operand_value(vm, instr->StructElementPtr.address); + i32 elem_index = instr->StructElementPtr.elem_index; + + Type *t = ssa_type(instr->StructElementPtr.address); + GB_ASSERT(is_type_pointer(t)); + i64 offset = vm_type_offset_of(vm, type_deref(t), elem_index); + void *ptr = cast(u8 *)address.val_ptr + offset; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_PtrOffset: { + Type *t = ssa_type(instr->PtrOffset.address); + GB_ASSERT(is_type_pointer(t)); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + vmValue address = vm_operand_value(vm, instr->PtrOffset.address); + vmValue offset = vm_operand_value(vm, instr->PtrOffset.offset); + + void *ptr = cast(u8 *)address.val_ptr + offset.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_Phi: { + for_array(i, f->curr_block->preds) { + ssaBlock *pred = f->curr_block->preds[i]; + if (f->prev_block == pred) { + vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]); + vm_set_value(f, value, edge); + break; + } + } + } break; + + case ssaInstr_ArrayExtractValue: { + vmValue s = vm_operand_value(vm, instr->ArrayExtractValue.address); + vmValue v = s.val_comp[instr->ArrayExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_StructExtractValue: { + vmValue s = vm_operand_value(vm, instr->StructExtractValue.address); + vmValue v = s.val_comp[instr->StructExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_Jump: { + vm_jump_block(f, instr->Jump.block); + } break; + + case ssaInstr_If: { + vmValue cond = vm_operand_value(vm, instr->If.cond); + if (cond.val_int != 0) { + vm_jump_block(f, instr->If.true_block); + } else { + vm_jump_block(f, instr->If.false_block); + } + } break; + + case ssaInstr_Return: { + Type *return_type = NULL; + vmValue result = {0}; + + if (instr->Return.value != NULL) { + return_type = ssa_type(instr->Return.value); + result = vm_operand_value(vm, instr->Return.value); + } + + f->result = result; + f->curr_block = NULL; + f->instr_index = 0; + return; + } break; + + case ssaInstr_Conv: { + // TODO(bill): Assuming little endian + vmValue dst = {0}; + vmValue src = vm_operand_value(vm, instr->Conv.value); + i64 from_size = vm_type_size_of(vm, instr->Conv.from); + i64 to_size = vm_type_size_of(vm, instr->Conv.to); + switch (instr->Conv.kind) { + case ssaConv_trunc: + gb_memcopy(&dst, &src, to_size); + break; + case ssaConv_zext: + gb_memcopy(&dst, &src, from_size); + break; + case ssaConv_fptrunc: { + GB_ASSERT(from_size > to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f64); + GB_ASSERT(base_type(instr->Conv.to) == t_f32); + dst.val_f32 = cast(f32)src.val_f64; + } break; + case ssaConv_fpext: { + GB_ASSERT(from_size < to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f32); + GB_ASSERT(base_type(instr->Conv.to) == t_f64); + dst.val_f64 = cast(f64)src.val_f32; + } break; + case ssaConv_fptoui: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + u64 u = cast(u64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } else { + u64 u = cast(u64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } + } break; + case ssaConv_fptosi: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + i64 i = cast(i64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } else { + i64 i = cast(i64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } + } break; + case ssaConv_uitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(u64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(u64)src.val_int); + } + } break; + case ssaConv_sitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(i64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(i64)src.val_int); + } + } break; + + case ssaConv_ptrtoint: + dst = vm_make_value_int(instr->Conv.to, cast(i64)src.val_ptr); + break; + case ssaConv_inttoptr: + dst = vm_make_value_ptr(instr->Conv.to, cast(void *)src.val_int); + break; + case ssaConv_bitcast: + dst = src; + dst.type = instr->Conv.to; + break; + } + + vm_set_value(f, value, dst); + } break; + + case ssaInstr_Unreachable: { + GB_PANIC("Unreachable"); + } break; + + case ssaInstr_BinaryOp: { + ssaInstrBinaryOp *bo = &instr->BinaryOp; + Type *type = ssa_type(bo->left); + vmValue lhs = vm_operand_value(vm, bo->left); + vmValue rhs = vm_operand_value(vm, bo->right); + vmValue v = vm_exec_binary_op(vm, type, lhs, rhs, bo->op); + vm_set_value(f, value, v); + } break; + + case ssaInstr_Call: { + Array args = {0}; + array_init(&args, f->stack_allocator, instr->Call.arg_count); + for (isize i = 0; i < instr->Call.arg_count; i++) { + array_add(&args, vm_operand_value(vm, instr->Call.args[i])); + } + vmValue proc = vm_operand_value(vm, instr->Call.value); + if (proc.val_proc.proc != NULL) { + vmValue result = vm_call_proc(vm, proc.val_proc.proc, args); + vm_set_value(f, value, result); + } else { + GB_PANIC("TODO(bill): external procedure calls"); + } + + } break; + + case ssaInstr_Select: { + vmValue v = {0}; + vmValue cond = vm_operand_value(vm, instr->Select.cond); + if (cond.val_int != 0) { + v = vm_operand_value(vm, instr->Select.true_value); + } else { + v = vm_operand_value(vm, instr->Select.false_value); + } + + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorExtractElement: { + vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector); + vmValue index = vm_operand_value(vm, instr->VectorExtractElement.index); + vmValue v = vector.val_comp[index.val_int]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorInsertElement: { + vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector); + vmValue elem = vm_operand_value(vm, instr->VectorInsertElement.elem); + vmValue index = vm_operand_value(vm, instr->VectorInsertElement.index); + vector.val_comp[index.val_int] = elem; + } break; + + case ssaInstr_VectorShuffle: { + ssaValueVectorShuffle *vs = &instr->VectorShuffle; + vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector); + vmValue new_vector = vm_make_value_comp(ssa_type(value), vm->stack_allocator, vs->index_count); + + for (i32 i = 0; i < vs->index_count; i++) { + new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]]; + } + + vm_set_value(f, value, new_vector); + } break; + + case ssaInstr_BoundsCheck: { + ssaInstrBoundsCheck *bc = &instr->BoundsCheck; + Array args = {0}; + array_init(&args, vm->stack_allocator, 5); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->index)); + array_add(&args, vm_operand_value(vm, bc->len)); + + vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args); + } break; + + case ssaInstr_SliceBoundsCheck: { + ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; + Array args = {0}; + + array_init(&args, vm->stack_allocator, 7); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->low)); + array_add(&args, vm_operand_value(vm, bc->high)); + if (!bc->is_substring) { + array_add(&args, vm_operand_value(vm, bc->max)); + vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args); + } else { + vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args); + } + } break; + + default: { + GB_PANIC(" %d\n", instr->kind); + } break; + } +} + + + +void vm_print_value(vmValue value, Type *type) { + type = base_type(type); + if (is_type_string(type)) { + vmValue data = value.val_comp[0]; + vmValue count = value.val_comp[1]; + gb_printf("`%.*s`", cast(int)count.val_int, cast(u8 *)data.val_ptr); + } else if (is_type_boolean(type)) { + if (value.val_int != 0) { + gb_printf("true"); + } else { + gb_printf("false"); + } + } else if (is_type_integer(type)) { + gb_printf("%lld", cast(i64)value.val_int); + } else if (type == t_f32) { + gb_printf("%f", value.val_f32); + } else if (type == t_f64) { + gb_printf("%f", value.val_f64); + } else if (is_type_pointer(type)) { + gb_printf("0x%08x", value.val_ptr); + } else if (is_type_array(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Array.elem); + } + gb_printf("]"); + } else if (is_type_vector(type)) { + gb_printf("<"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Vector.elem); + } + gb_printf(">"); + } else if (is_type_slice(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Slice.elem); + } + gb_printf("]"); + } else if (is_type_maybe(type)) { + if (value.val_comp[1].val_int != 0) { + gb_printf("?"); + vm_print_value(value.val_comp[0], type->Maybe.elem); + } else { + gb_printf("nil"); + } + } else if (is_type_struct(type)) { + if (value.val_comp.count == 0) { + gb_printf("nil"); + } else { + gb_printf("{"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Record.fields[i]->type); + } + gb_printf("}"); + } + } else if (is_type_tuple(type)) { + if (value.val_comp.count != 1) { + gb_printf("("); + } + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type); + } + if (value.val_comp.count != 1) { + gb_printf(")"); + } + } +} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 000000000..1ea8ba430 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,3250 @@ +typedef struct AstNode AstNode; +typedef struct Scope Scope; +typedef struct DeclInfo DeclInfo; + +typedef enum ParseFileError { + ParseFile_None, + + ParseFile_WrongExtension, + ParseFile_InvalidFile, + ParseFile_EmptyFile, + ParseFile_Permission, + ParseFile_NotFound, + ParseFile_InvalidToken, + + ParseFile_Count, +} ParseFileError; + +typedef Array(AstNode *) AstNodeArray; + +typedef struct AstFile { + i32 id; + gbArena arena; + Tokenizer tokenizer; + Array(Token) tokens; + isize curr_token_index; + Token curr_token; + Token prev_token; // previous non-comment + + // >= 0: In Expression + // < 0: In Control Clause + // NOTE(bill): Used to prevent type literals in control clauses + isize expr_level; + + AstNodeArray decls; + bool is_global_scope; + + 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 +#define PARSER_MAX_FIX_COUNT 6 + isize fix_count; + TokenPos fix_prev_pos; +} AstFile; + +typedef struct ImportedFile { + String path; + String rel_path; + TokenPos pos; // #import +} ImportedFile; + +typedef struct Parser { + String init_fullpath; + Array(AstFile) files; + Array(ImportedFile) imports; + gbAtomic32 import_index; + Array(String) foreign_libraries; + isize total_token_count; + gbMutex mutex; +} Parser; + +typedef enum ProcTag { + ProcTag_bounds_check = GB_BIT(0), + ProcTag_no_bounds_check = GB_BIT(1), + + ProcTag_foreign = GB_BIT(10), + ProcTag_link_name = GB_BIT(11), + ProcTag_inline = GB_BIT(12), + ProcTag_no_inline = GB_BIT(13), + ProcTag_dll_import = GB_BIT(14), + ProcTag_dll_export = GB_BIT(15), + + ProcTag_stdcall = GB_BIT(16), + ProcTag_fastcall = GB_BIT(17), + // ProcTag_cdecl = GB_BIT(18), +} ProcTag; + +typedef enum VarDeclTag { + VarDeclTag_thread_local = GB_BIT(0), +} VarDeclTag; + +typedef enum StmtStateFlag { + StmtStateFlag_bounds_check = GB_BIT(0), + StmtStateFlag_no_bounds_check = GB_BIT(1), +} StmtStateFlag; + + +typedef enum CallExprKind { + CallExpr_Prefix, // call(...) + CallExpr_Postfix, // a'call + CallExpr_Infix, // a ''call b +} CallExprKind; + +AstNodeArray make_ast_node_array(AstFile *f) { + AstNodeArray a; + array_init(&a, gb_arena_allocator(&f->arena)); + return a; +} + + +#define AST_NODE_KINDS \ + AST_NODE_KIND(BasicLit, "basic literal", Token) \ + AST_NODE_KIND(Ident, "identifier", Token) \ + AST_NODE_KIND(Ellipsis, "ellipsis", struct { \ + Token token; \ + AstNode *expr; \ + }) \ + AST_NODE_KIND(ProcLit, "procedure literal", struct { \ + AstNode *type; \ + AstNode *body; \ + u64 tags; \ + }) \ + AST_NODE_KIND(CompoundLit, "compound literal", struct { \ + AstNode *type; \ + AstNodeArray elems; \ + Token open, close; \ + }) \ +AST_NODE_KIND(_ExprBegin, "", i32) \ + AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ + AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \ + AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \ + AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \ + AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \ + AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ + AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ + AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(DemaybeExpr, "demaybe expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(CallExpr, "call expression", struct { \ + AstNode *proc; \ + AstNodeArray args; \ + Token open, close; \ + Token ellipsis; \ + CallExprKind kind; \ + }) \ + AST_NODE_KIND(SliceExpr, "slice expression", struct { \ + AstNode *expr; \ + Token open, close; \ + AstNode *low, *high, *max; \ + bool triple_indexed; \ + }) \ + AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ +AST_NODE_KIND(_ExprEnd, "", i32) \ +AST_NODE_KIND(_StmtBegin, "", i32) \ + AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ + AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \ + AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \ + AST_NODE_KIND(IncDecStmt, "increment/decrement statement", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(TagStmt, "tag statement", struct { \ + Token token; \ + Token name; \ + AstNode *stmt; \ + }) \ + AST_NODE_KIND(AssignStmt, "assign statement", struct { \ + Token op; \ + AstNodeArray lhs, rhs; \ + }) \ +AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ + AST_NODE_KIND(BlockStmt, "block statement", struct { \ + AstNodeArray stmts; \ + Token open, close; \ + }) \ + AST_NODE_KIND(IfStmt, "if statement", struct { \ + Token token; \ + AstNode *init; \ + AstNode *cond; \ + AstNode *body; \ + AstNode *else_stmt; \ + }) \ + AST_NODE_KIND(ReturnStmt, "return statement", struct { \ + Token token; \ + AstNodeArray results; \ + }) \ + AST_NODE_KIND(ForStmt, "for statement", struct { \ + Token token; \ + AstNode *init, *cond, *post; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(CaseClause, "case clause", struct { \ + Token token; \ + AstNodeArray list, stmts; \ + }) \ + AST_NODE_KIND(MatchStmt, "match statement", struct { \ + Token token; \ + 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; }) \ + AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ + Token string; \ + AstNode *operand; \ + }) \ + AST_NODE_KIND(AsmStmt, "assembly statement", struct { \ + Token token; \ + bool is_volatile; \ + Token open, close; \ + Token code_string; \ + AstNode *output_list; \ + AstNode *input_list; \ + AstNode *clobber_list; \ + isize output_count, input_count, clobber_count; \ + }) \ + AST_NODE_KIND(PushAllocator, "push_allocator statement", struct { \ + Token token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(PushContext, "push_context statement", struct { \ + Token token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ +\ +AST_NODE_KIND(_ComplexStmtEnd, "", i32) \ +AST_NODE_KIND(_StmtEnd, "", i32) \ +AST_NODE_KIND(_DeclBegin, "", i32) \ + AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \ + AST_NODE_KIND(VarDecl, "variable declaration", struct { \ + u64 tags; \ + bool is_using; \ + AstNodeArray names; \ + AstNode * type; \ + AstNodeArray values; \ + AstNode * note; \ + }) \ + AST_NODE_KIND(ConstDecl, "constant declaration", struct { \ + u64 tags; \ + AstNodeArray names; \ + AstNode * type; \ + AstNodeArray values; \ + AstNode * note; \ + }) \ + AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \ + AstNode *name; \ + AstNode *type; \ + AstNode *body; \ + u64 tags; \ + String foreign_name; \ + String link_name; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(TypeDecl, "type declaration", struct { \ + Token token; \ + AstNode *name, *type; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(ImportDecl, "import declaration", struct { \ + Token token, relpath; \ + String fullpath; \ + Token import_name; \ + bool is_load; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \ + Token token, filepath; \ + bool is_system; \ + }) \ +AST_NODE_KIND(_DeclEnd, "", i32) \ +AST_NODE_KIND(_TypeBegin, "", i32) \ + AST_NODE_KIND(Parameter, "parameter", struct { \ + AstNodeArray names; \ + AstNode *type; \ + bool is_using; \ + }) \ + AST_NODE_KIND(ProcType, "procedure type", struct { \ + Token token; \ + AstNodeArray params; \ + AstNodeArray results; \ + }) \ + AST_NODE_KIND(PointerType, "pointer type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(MaybeType, "maybe type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(ArrayType, "array type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(VectorType, "vector type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(StructType, "struct type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + bool is_packed; \ + bool is_ordered; \ + }) \ + AST_NODE_KIND(UnionType, "union type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + }) \ + AST_NODE_KIND(RawUnionType, "raw union type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + }) \ + AST_NODE_KIND(EnumType, "enum type", struct { \ + Token token; \ + AstNode *base_type; \ + AstNodeArray fields; \ + }) \ +AST_NODE_KIND(_TypeEnd, "", i32) + +typedef enum AstNodeKind { + AstNode_Invalid, +#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_), + AST_NODE_KINDS +#undef AST_NODE_KIND + AstNode_Count, +} AstNodeKind; + +String const ast_node_strings[] = { + {cast(u8 *)"invalid node", gb_size_of("invalid node")}, +#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1}, + AST_NODE_KINDS +#undef AST_NODE_KIND +}; + +#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_); + AST_NODE_KINDS +#undef AST_NODE_KIND + +typedef struct AstNode { + AstNodeKind kind; + // AstNode *prev, *next; // NOTE(bill): allow for Linked list + u32 stmt_state_flags; + union { +#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; + AST_NODE_KINDS +#undef AST_NODE_KIND + }; +} AstNode; + + +#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_)) +#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_); +#define case_end } break; + + + + +gb_inline bool is_ast_node_expr(AstNode *node) { + return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); +} +gb_inline bool is_ast_node_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1); +} +gb_inline bool is_ast_node_complex_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1); +} +gb_inline bool is_ast_node_decl(AstNode *node) { + return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1); +} +gb_inline bool is_ast_node_type(AstNode *node) { + return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1); +} + + +Token ast_node_token(AstNode *node) { + switch (node->kind) { + case AstNode_BasicLit: + return node->BasicLit; + case AstNode_Ident: + return node->Ident; + case AstNode_ProcLit: + return ast_node_token(node->ProcLit.type); + case AstNode_CompoundLit: + if (node->CompoundLit.type != NULL) { + return ast_node_token(node->CompoundLit.type); + } + return node->CompoundLit.open; + case AstNode_TagExpr: + return node->TagExpr.token; + case AstNode_RunExpr: + return node->RunExpr.token; + case AstNode_BadExpr: + return node->BadExpr.begin; + case AstNode_UnaryExpr: + return node->UnaryExpr.op; + case AstNode_BinaryExpr: + return ast_node_token(node->BinaryExpr.left); + case AstNode_ParenExpr: + return node->ParenExpr.open; + case AstNode_CallExpr: + return ast_node_token(node->CallExpr.proc); + case AstNode_SelectorExpr: + return ast_node_token(node->SelectorExpr.selector); + case AstNode_IndexExpr: + return node->IndexExpr.open; + case AstNode_SliceExpr: + return node->SliceExpr.open; + case AstNode_Ellipsis: + return node->Ellipsis.token; + case AstNode_FieldValue: + return node->FieldValue.eq; + case AstNode_DerefExpr: + return node->DerefExpr.op; + case AstNode_DemaybeExpr: + return node->DemaybeExpr.op; + case AstNode_BadStmt: + return node->BadStmt.begin; + case AstNode_EmptyStmt: + return node->EmptyStmt.token; + case AstNode_ExprStmt: + return ast_node_token(node->ExprStmt.expr); + case AstNode_TagStmt: + return node->TagStmt.token; + case AstNode_IncDecStmt: + return node->IncDecStmt.op; + case AstNode_AssignStmt: + return node->AssignStmt.op; + case AstNode_BlockStmt: + return node->BlockStmt.open; + case AstNode_IfStmt: + return node->IfStmt.token; + case AstNode_ReturnStmt: + return node->ReturnStmt.token; + case AstNode_ForStmt: + return node->ForStmt.token; + case AstNode_MatchStmt: + return node->MatchStmt.token; + case AstNode_CaseClause: + return node->CaseClause.token; + case AstNode_DeferStmt: + return node->DeferStmt.token; + case AstNode_BranchStmt: + return node->BranchStmt.token; + case AstNode_UsingStmt: + return node->UsingStmt.token; + case AstNode_AsmStmt: + return node->AsmStmt.token; + case AstNode_PushAllocator: + return node->PushAllocator.token; + case AstNode_PushContext: + return node->PushContext.token; + case AstNode_BadDecl: + return node->BadDecl.begin; + case AstNode_VarDecl: + return ast_node_token(node->VarDecl.names.e[0]); + case AstNode_ConstDecl: + return ast_node_token(node->ConstDecl.names.e[0]); + case AstNode_ProcDecl: + return node->ProcDecl.name->Ident; + case AstNode_TypeDecl: + return node->TypeDecl.token; + case AstNode_ImportDecl: + return node->ImportDecl.token; + case AstNode_ForeignLibrary: + return node->ForeignLibrary.token; + case AstNode_Parameter: { + if (node->Parameter.names.count > 0) { + return ast_node_token(node->Parameter.names.e[0]); + } else { + return ast_node_token(node->Parameter.type); + } + } + case AstNode_ProcType: + return node->ProcType.token; + case AstNode_PointerType: + return node->PointerType.token; + case AstNode_MaybeType: + return node->MaybeType.token; + case AstNode_ArrayType: + return node->ArrayType.token; + case AstNode_VectorType: + return node->VectorType.token; + case AstNode_StructType: + return node->StructType.token; + case AstNode_UnionType: + return node->UnionType.token; + case AstNode_RawUnionType: + return node->RawUnionType.token; + case AstNode_EnumType: + return node->EnumType.token; + } + + return empty_token; +} + +// 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; + if (gb_arena_size_remaining(arena, GB_DEFAULT_MEMORY_ALIGNMENT) <= gb_size_of(AstNode)) { + // NOTE(bill): If a syntax error is so bad, just quit! + gb_exit(1); + } + AstNode *node = gb_alloc_item(gb_arena_allocator(arena), AstNode); + node->kind = kind; + return node; +} + +AstNode *make_bad_expr(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadExpr); + result->BadExpr.begin = begin; + result->BadExpr.end = end; + return result; +} + +AstNode *make_tag_expr(AstFile *f, Token token, Token name, AstNode *expr) { + AstNode *result = make_node(f, AstNode_TagExpr); + result->TagExpr.token = token; + result->TagExpr.name = name; + result->TagExpr.expr = expr; + return result; +} + +AstNode *make_run_expr(AstFile *f, Token token, Token name, AstNode *expr) { + AstNode *result = make_node(f, AstNode_RunExpr); + result->RunExpr.token = token; + result->RunExpr.name = name; + result->RunExpr.expr = expr; + return result; +} + + +AstNode *make_tag_stmt(AstFile *f, Token token, Token name, AstNode *stmt) { + AstNode *result = make_node(f, AstNode_TagStmt); + result->TagStmt.token = token; + result->TagStmt.name = name; + result->TagStmt.stmt = stmt; + return result; +} + +AstNode *make_unary_expr(AstFile *f, Token op, AstNode *expr) { + AstNode *result = make_node(f, AstNode_UnaryExpr); + result->UnaryExpr.op = op; + result->UnaryExpr.expr = expr; + return result; +} + +AstNode *make_binary_expr(AstFile *f, Token op, AstNode *left, AstNode *right) { + AstNode *result = make_node(f, AstNode_BinaryExpr); + + if (left == NULL) { + syntax_error(op, "No lhs expression for binary expression `%.*s`", LIT(op.string)); + left = make_bad_expr(f, op, op); + } + if (right == NULL) { + syntax_error(op, "No rhs expression for binary expression `%.*s`", LIT(op.string)); + right = make_bad_expr(f, op, op); + } + + result->BinaryExpr.op = op; + result->BinaryExpr.left = left; + result->BinaryExpr.right = right; + + return result; +} + +AstNode *make_paren_expr(AstFile *f, AstNode *expr, Token open, Token close) { + AstNode *result = make_node(f, AstNode_ParenExpr); + result->ParenExpr.expr = expr; + result->ParenExpr.open = open; + result->ParenExpr.close = close; + return result; +} + +AstNode *make_call_expr(AstFile *f, AstNode *proc, AstNodeArray args, Token open, Token close, Token ellipsis) { + AstNode *result = make_node(f, AstNode_CallExpr); + result->CallExpr.proc = proc; + result->CallExpr.args = args; + result->CallExpr.open = open; + result->CallExpr.close = close; + result->CallExpr.ellipsis = ellipsis; + return result; +} + +AstNode *make_selector_expr(AstFile *f, Token token, AstNode *expr, AstNode *selector) { + AstNode *result = make_node(f, AstNode_SelectorExpr); + result->SelectorExpr.expr = expr; + result->SelectorExpr.selector = selector; + return result; +} + +AstNode *make_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, Token close) { + AstNode *result = make_node(f, AstNode_IndexExpr); + result->IndexExpr.expr = expr; + result->IndexExpr.index = index; + result->IndexExpr.open = open; + result->IndexExpr.close = close; + return result; +} + + +AstNode *make_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, AstNode *low, AstNode *high, AstNode *max, bool triple_indexed) { + AstNode *result = make_node(f, AstNode_SliceExpr); + result->SliceExpr.expr = expr; + result->SliceExpr.open = open; + result->SliceExpr.close = close; + result->SliceExpr.low = low; + result->SliceExpr.high = high; + result->SliceExpr.max = max; + result->SliceExpr.triple_indexed = triple_indexed; + return result; +} + +AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) { + AstNode *result = make_node(f, AstNode_DerefExpr); + result->DerefExpr.expr = expr; + result->DerefExpr.op = op; + return result; +} + +AstNode *make_demaybe_expr(AstFile *f, AstNode *expr, Token op) { + AstNode *result = make_node(f, AstNode_DemaybeExpr); + result->DemaybeExpr.expr = expr; + result->DemaybeExpr.op = op; + return result; +} + + +AstNode *make_basic_lit(AstFile *f, Token basic_lit) { + AstNode *result = make_node(f, AstNode_BasicLit); + result->BasicLit = basic_lit; + return result; +} + +AstNode *make_ident(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_Ident); + result->Ident = token; + return result; +} + +AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) { + AstNode *result = make_node(f, AstNode_Ellipsis); + result->Ellipsis.token = token; + result->Ellipsis.expr = expr; + return result; +} + + +AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) { + AstNode *result = make_node(f, AstNode_ProcLit); + result->ProcLit.type = type; + result->ProcLit.body = body; + result->ProcLit.tags = tags; + return result; +} + +AstNode *make_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) { + AstNode *result = make_node(f, AstNode_FieldValue); + result->FieldValue.field = field; + result->FieldValue.value = value; + result->FieldValue.eq = eq; + return result; +} + +AstNode *make_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) { + AstNode *result = make_node(f, AstNode_CompoundLit); + result->CompoundLit.type = type; + result->CompoundLit.elems = elems; + result->CompoundLit.open = open; + result->CompoundLit.close = close; + return result; +} + +AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadStmt); + result->BadStmt.begin = begin; + result->BadStmt.end = end; + return result; +} + +AstNode *make_empty_stmt(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_EmptyStmt); + result->EmptyStmt.token = token; + return result; +} + +AstNode *make_expr_stmt(AstFile *f, AstNode *expr) { + AstNode *result = make_node(f, AstNode_ExprStmt); + result->ExprStmt.expr = expr; + return result; +} + +AstNode *make_inc_dec_stmt(AstFile *f, Token op, AstNode *expr) { + AstNode *result = make_node(f, AstNode_IncDecStmt); + result->IncDecStmt.op = op; + result->IncDecStmt.expr = expr; + return result; +} + +AstNode *make_assign_stmt(AstFile *f, Token op, AstNodeArray lhs, AstNodeArray rhs) { + AstNode *result = make_node(f, AstNode_AssignStmt); + result->AssignStmt.op = op; + result->AssignStmt.lhs = lhs; + result->AssignStmt.rhs = rhs; + return result; +} + +AstNode *make_block_stmt(AstFile *f, AstNodeArray stmts, Token open, Token close) { + AstNode *result = make_node(f, AstNode_BlockStmt); + result->BlockStmt.stmts = stmts; + result->BlockStmt.open = open; + result->BlockStmt.close = close; + return result; +} + +AstNode *make_if_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_stmt) { + AstNode *result = make_node(f, AstNode_IfStmt); + result->IfStmt.token = token; + result->IfStmt.init = init; + result->IfStmt.cond = cond; + result->IfStmt.body = body; + result->IfStmt.else_stmt = else_stmt; + return result; +} + +AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) { + AstNode *result = make_node(f, AstNode_ReturnStmt); + result->ReturnStmt.token = token; + result->ReturnStmt.results = results; + return result; +} + +AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { + AstNode *result = make_node(f, AstNode_ForStmt); + result->ForStmt.token = token; + result->ForStmt.init = init; + result->ForStmt.cond = cond; + result->ForStmt.post = post; + result->ForStmt.body = body; + return result; +} + + +AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) { + AstNode *result = make_node(f, AstNode_MatchStmt); + result->MatchStmt.token = token; + result->MatchStmt.init = init; + result->MatchStmt.tag = tag; + result->MatchStmt.body = body; + return result; +} + + +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; +} + +AstNode *make_case_clause(AstFile *f, Token token, AstNodeArray list, AstNodeArray stmts) { + AstNode *result = make_node(f, AstNode_CaseClause); + result->CaseClause.token = token; + result->CaseClause.list = list; + result->CaseClause.stmts = stmts; + return result; +} + + +AstNode *make_defer_stmt(AstFile *f, Token token, AstNode *stmt) { + AstNode *result = make_node(f, AstNode_DeferStmt); + result->DeferStmt.token = token; + result->DeferStmt.stmt = stmt; + return result; +} + +AstNode *make_branch_stmt(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_BranchStmt); + result->BranchStmt.token = token; + return result; +} + +AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) { + AstNode *result = make_node(f, AstNode_UsingStmt); + result->UsingStmt.token = token; + result->UsingStmt.node = node; + return result; +} + +AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) { + AstNode *result = make_node(f, AstNode_AsmOperand); + result->AsmOperand.string = string; + result->AsmOperand.operand = operand; + return result; + +} + +AstNode *make_asm_stmt(AstFile *f, Token token, bool is_volatile, Token open, Token close, Token code_string, + AstNode *output_list, AstNode *input_list, AstNode *clobber_list, + isize output_count, isize input_count, isize clobber_count) { + AstNode *result = make_node(f, AstNode_AsmStmt); + result->AsmStmt.token = token; + result->AsmStmt.is_volatile = is_volatile; + result->AsmStmt.open = open; + result->AsmStmt.close = close; + result->AsmStmt.code_string = code_string; + result->AsmStmt.output_list = output_list; + result->AsmStmt.input_list = input_list; + result->AsmStmt.clobber_list = clobber_list; + result->AsmStmt.output_count = output_count; + result->AsmStmt.input_count = input_count; + result->AsmStmt.clobber_count = clobber_count; + return result; +} + +AstNode *make_push_allocator(AstFile *f, Token token, AstNode *expr, AstNode *body) { + AstNode *result = make_node(f, AstNode_PushAllocator); + result->PushAllocator.token = token; + result->PushAllocator.expr = expr; + result->PushAllocator.body = body; + return result; +} + +AstNode *make_push_context(AstFile *f, Token token, AstNode *expr, AstNode *body) { + AstNode *result = make_node(f, AstNode_PushContext); + result->PushContext.token = token; + result->PushContext.expr = expr; + result->PushContext.body = body; + return result; +} + + + + +AstNode *make_bad_decl(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadDecl); + result->BadDecl.begin = begin; + result->BadDecl.end = end; + return result; +} + +AstNode *make_var_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) { + AstNode *result = make_node(f, AstNode_VarDecl); + result->VarDecl.names = names; + result->VarDecl.type = type; + result->VarDecl.values = values; + return result; +} + +AstNode *make_const_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) { + AstNode *result = make_node(f, AstNode_ConstDecl); + result->ConstDecl.names = names; + result->ConstDecl.type = type; + result->ConstDecl.values = values; + return result; +} + +AstNode *make_parameter(AstFile *f, AstNodeArray names, AstNode *type, bool is_using) { + AstNode *result = make_node(f, AstNode_Parameter); + result->Parameter.names = names; + result->Parameter.type = type; + result->Parameter.is_using = is_using; + return result; +} + +AstNode *make_proc_type(AstFile *f, Token token, AstNodeArray params, AstNodeArray results) { + AstNode *result = make_node(f, AstNode_ProcType); + result->ProcType.token = token; + result->ProcType.params = params; + result->ProcType.results = results; + return result; +} + +AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String link_name) { + AstNode *result = make_node(f, AstNode_ProcDecl); + result->ProcDecl.name = name; + result->ProcDecl.type = proc_type; + result->ProcDecl.body = body; + result->ProcDecl.tags = tags; + result->ProcDecl.foreign_name = foreign_name; + result->ProcDecl.link_name = link_name; + return result; +} + +AstNode *make_pointer_type(AstFile *f, Token token, AstNode *type) { + AstNode *result = make_node(f, AstNode_PointerType); + result->PointerType.token = token; + result->PointerType.type = type; + return result; +} + +AstNode *make_maybe_type(AstFile *f, Token token, AstNode *type) { + AstNode *result = make_node(f, AstNode_MaybeType); + result->MaybeType.token = token; + result->MaybeType.type = type; + return result; +} + +AstNode *make_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { + AstNode *result = make_node(f, AstNode_ArrayType); + result->ArrayType.token = token; + result->ArrayType.count = count; + result->ArrayType.elem = elem; + return result; +} + +AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { + AstNode *result = make_node(f, AstNode_VectorType); + result->VectorType.token = token; + result->VectorType.count = count; + result->VectorType.elem = elem; + return result; +} + +AstNode *make_struct_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count, bool is_packed, bool is_ordered) { + AstNode *result = make_node(f, AstNode_StructType); + result->StructType.token = token; + result->StructType.decls = decls; + result->StructType.decl_count = decl_count; + result->StructType.is_packed = is_packed; + result->StructType.is_ordered = is_ordered; + return result; +} + + +AstNode *make_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) { + AstNode *result = make_node(f, AstNode_UnionType); + result->UnionType.token = token; + result->UnionType.decls = decls; + result->UnionType.decl_count = decl_count; + return result; +} + +AstNode *make_raw_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) { + AstNode *result = make_node(f, AstNode_RawUnionType); + result->RawUnionType.token = token; + result->RawUnionType.decls = decls; + result->RawUnionType.decl_count = decl_count; + return result; +} + + +AstNode *make_enum_type(AstFile *f, Token token, AstNode *base_type, AstNodeArray fields) { + AstNode *result = make_node(f, AstNode_EnumType); + result->EnumType.token = token; + result->EnumType.base_type = base_type; + result->EnumType.fields = fields; + return result; +} + +AstNode *make_type_decl(AstFile *f, Token token, AstNode *name, AstNode *type) { + AstNode *result = make_node(f, AstNode_TypeDecl); + result->TypeDecl.token = token; + result->TypeDecl.name = name; + result->TypeDecl.type = type; + return result; +} + +AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name, bool is_load) { + AstNode *result = make_node(f, AstNode_ImportDecl); + result->ImportDecl.token = token; + result->ImportDecl.relpath = relpath; + result->ImportDecl.import_name = import_name; + result->ImportDecl.is_load = is_load; + return result; +} + +AstNode *make_foreign_library(AstFile *f, Token token, Token filepath, bool is_system) { + AstNode *result = make_node(f, AstNode_ForeignLibrary); + result->ForeignLibrary.token = token; + result->ForeignLibrary.filepath = filepath; + result->ForeignLibrary.is_system = is_system; + return result; +} + +bool next_token(AstFile *f) { + if (f->curr_token_index+1 < f->tokens.count) { + if (f->curr_token.kind != Token_Comment) { + f->prev_token = f->curr_token; + } + + f->curr_token_index++; + f->curr_token = f->tokens.e[f->curr_token_index]; + if (f->curr_token.kind == Token_Comment) { + return next_token(f); + } + return true; + } + syntax_error(f->curr_token, "Token is EOF"); + return false; +} + +Token expect_token(AstFile *f, TokenKind kind) { + Token prev = f->curr_token; + if (prev.kind != kind) { + syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`", + LIT(token_strings[kind]), + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +Token expect_token_after(AstFile *f, TokenKind kind, char *msg) { + Token prev = f->curr_token; + if (prev.kind != kind) { + syntax_error(f->curr_token, "Expected `%.*s` after %s, got `%.*s`", + LIT(token_strings[kind]), + msg, + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + + +Token expect_operator(AstFile *f) { + Token prev = f->curr_token; + if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { + syntax_error(f->curr_token, "Expected an operator, got `%.*s`", + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +Token expect_keyword(AstFile *f) { + Token prev = f->curr_token; + if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) { + syntax_error(f->curr_token, "Expected a keyword, got `%.*s`", + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +bool allow_token(AstFile *f, TokenKind kind) { + Token prev = f->curr_token; + if (prev.kind == kind) { + next_token(f); + return true; + } + return false; +} + + +bool is_blank_ident(String str) { + if (str.len == 1) { + return str.text[0] == '_'; + } + return false; +} + + +// NOTE(bill): Go to next statement to prevent numerous error messages popping up +void fix_advance_to_next_stmt(AstFile *f) { + // TODO(bill): fix_advance_to_next_stmt +#if 1 + for (;;) { + Token t = f->curr_token; + switch (t.kind) { + case Token_EOF: + case Token_Semicolon: + return; + + case Token_if: + case Token_return: + case Token_for: + case Token_match: + case Token_defer: + case Token_asm: + case Token_using: + + case Token_break: + case Token_continue: + case Token_fallthrough: + + case Token_push_allocator: + case Token_push_context: + + case Token_Hash: + { + if (token_pos_are_equal(t.pos, f->fix_prev_pos) && + f->fix_count < PARSER_MAX_FIX_COUNT) { + f->fix_count++; + return; + } + if (token_pos_cmp(f->fix_prev_pos, t.pos) < 0) { + f->fix_prev_pos = t.pos; + f->fix_count = 0; // NOTE(bill): Reset + return; + } + // NOTE(bill): Reaching here means there is a parsing bug + } break; + } + next_token(f); + } +#endif +} + +bool expect_semicolon_after_stmt(AstFile *f, AstNode *s) { + if (allow_token(f, Token_Semicolon)) { + return true; + } + + if (f->curr_token.pos.line != f->prev_token.pos.line) { + return true; + } + + switch (f->curr_token.kind) { + case Token_EOF: + case Token_CloseBrace: + return true; + } + + syntax_error(f->curr_token, + "Expected `;` after %.*s, got `%.*s`", + LIT(ast_node_strings[s->kind]), LIT(token_strings[f->curr_token.kind])); + fix_advance_to_next_stmt(f); + return false; +} + + +AstNode * parse_expr(AstFile *f, bool lhs); +AstNode * parse_proc_type(AstFile *f); +AstNodeArray parse_stmt_list(AstFile *f); +AstNode * parse_stmt(AstFile *f); +AstNode * parse_body(AstFile *f); + +AstNode *parse_identifier(AstFile *f) { + Token token = f->curr_token; + if (token.kind == Token_Identifier) { + next_token(f); + } else { + token.string = str_lit("_"); + expect_token(f, Token_Identifier); + } + return make_ident(f, token); +} + +AstNode *parse_tag_expr(AstFile *f, AstNode *expression) { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + return make_tag_expr(f, token, name, expression); +} + +AstNode *parse_tag_stmt(AstFile *f, AstNode *statement) { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + return make_tag_stmt(f, token, name, statement); +} + +AstNode *unparen_expr(AstNode *node) { + for (;;) { + if (node->kind != AstNode_ParenExpr) + return node; + node = node->ParenExpr.expr; + } +} + +AstNode *parse_value(AstFile *f); + +AstNodeArray parse_element_list(AstFile *f) { + AstNodeArray elems = make_ast_node_array(f); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *elem = parse_value(f); + if (f->curr_token.kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + AstNode *value = parse_value(f); + elem = make_field_value(f, elem, value, eq); + } + + array_add(&elems, elem); + + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + return elems; +} + +AstNode *parse_literal_value(AstFile *f, AstNode *type) { + AstNodeArray elems = {0}; + Token open = expect_token(f, Token_OpenBrace); + f->expr_level++; + if (f->curr_token.kind != Token_CloseBrace) { + elems = parse_element_list(f); + } + f->expr_level--; + Token close = expect_token(f, Token_CloseBrace); + + return make_compound_lit(f, type, elems, open, close); +} + +AstNode *parse_value(AstFile *f) { + if (f->curr_token.kind == Token_OpenBrace) + return parse_literal_value(f, NULL); + + AstNode *value = parse_expr(f, false); + return value; +} + +AstNode *parse_identifier_or_type(AstFile *f, u32 flags); + + +void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) { + if (*tags & tag) { + syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name)); + } + *tags |= tag; +} + +bool is_foreign_name_valid(String name) { + // TODO(bill): is_foreign_name_valid + if (name.len == 0) + return false; + isize offset = 0; + while (offset < name.len) { + Rune rune; + isize remaining = name.len - offset; + isize width = gb_utf8_decode(name.text+offset, remaining, &rune); + if (rune == GB_RUNE_INVALID && width == 1) { + return false; + } else if (rune == GB_RUNE_BOM && remaining > 0) { + return false; + } + + if (offset == 0) { + switch (rune) { + case '-': + case '$': + case '.': + case '_': + break; + default: + if (!gb_char_is_alpha(cast(char)rune)) + return false; + break; + } + } else { + switch (rune) { + case '-': + case '$': + case '.': + case '_': + break; + default: + if (!gb_char_is_alphanumeric(cast(char)rune)) { + return false; + } + break; + } + } + + offset += width; + } + + return true; +} + +void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_name) { + // TODO(bill): Add this to procedure literals too + GB_ASSERT(foreign_name != NULL); + GB_ASSERT(link_name != NULL); + + while (f->curr_token.kind == Token_Hash) { + AstNode *tag_expr = parse_tag_expr(f, NULL); + ast_node(te, TagExpr, tag_expr); + String tag_name = te->name.string; + + #define ELSE_IF_ADD_TAG(name) \ + else if (str_eq(tag_name, str_lit(#name))) { \ + check_proc_add_tag(f, tag_expr, tags, ProcTag_##name, tag_name); \ + } + + if (str_eq(tag_name, str_lit("foreign"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_foreign, tag_name); + if (f->curr_token.kind == Token_String) { + *foreign_name = f->curr_token.string; + // TODO(bill): Check if valid string + if (!is_foreign_name_valid(*foreign_name)) { + syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name)); + } + + next_token(f); + } + } else if (str_eq(tag_name, str_lit("link_name"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name); + if (f->curr_token.kind == Token_String) { + *link_name = f->curr_token.string; + // TODO(bill): Check if valid string + if (!is_foreign_name_valid(*link_name)) { + syntax_error(ast_node_token(tag_expr), "Invalid alternative link procedure name `%.*s`", LIT(*link_name)); + } + + next_token(f); + } else { + expect_token(f, Token_String); + } + } + ELSE_IF_ADD_TAG(bounds_check) + ELSE_IF_ADD_TAG(no_bounds_check) + ELSE_IF_ADD_TAG(inline) + ELSE_IF_ADD_TAG(no_inline) + ELSE_IF_ADD_TAG(dll_import) + ELSE_IF_ADD_TAG(dll_export) + ELSE_IF_ADD_TAG(stdcall) + ELSE_IF_ADD_TAG(fastcall) + // ELSE_IF_ADD_TAG(cdecl) + else { + syntax_error(ast_node_token(tag_expr), "Unknown procedure tag"); + } + + #undef ELSE_IF_ADD_TAG + } + + if ((*tags & ProcTag_foreign) && (*tags & ProcTag_link_name)) { + syntax_error(f->curr_token, "You cannot apply both #foreign and #link_name to a procedure"); + } + + if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) { + syntax_error(f->curr_token, "You cannot apply both #inline and #no_inline to a procedure"); + } + + if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { + syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); + } + + if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) { + syntax_error(f->curr_token, "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body"); + } + + if ((*tags & ProcTag_stdcall) && (*tags & ProcTag_fastcall)) { + syntax_error(f->curr_token, "You cannot apply one calling convention to a procedure"); + } +} + +AstNode *parse_operand(AstFile *f, bool lhs) { + AstNode *operand = NULL; // Operand + switch (f->curr_token.kind) { + case Token_Identifier: + operand = parse_identifier(f); + if (!lhs) { + // TODO(bill): Handle? + } + return operand; + + case Token_Integer: + case Token_Float: + case Token_String: + case Token_Rune: + operand = make_basic_lit(f, f->curr_token); + next_token(f); + return operand; + + case Token_OpenParen: { + Token open, close; + // NOTE(bill): Skip the Paren Expression + open = expect_token(f, Token_OpenParen); + f->expr_level++; + operand = parse_expr(f, false); + f->expr_level--; + close = expect_token(f, Token_CloseParen); + return make_paren_expr(f, operand, open, close); + } + + case Token_Hash: { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + if (str_eq(name.string, str_lit("rune"))) { + if (f->curr_token.kind == Token_String) { + Token *s = &f->curr_token; + + if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) { + syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string)); + } + s->kind = Token_Rune; // NOTE(bill): Change it + } else { + expect_token(f, Token_String); + } + operand = parse_operand(f, lhs); + } else if (str_eq(name.string, str_lit("file"))) { + Token token = name; + token.kind = Token_String; + token.string = token.pos.file; + return make_basic_lit(f, token); + } else if (str_eq(name.string, str_lit("line"))) { + Token token = name; + token.kind = Token_Integer; + char *str = gb_alloc_array(gb_arena_allocator(&f->arena), char, 20); + gb_i64_to_str(token.pos.line, str, 10); + token.string = make_string_c(str); + return make_basic_lit(f, token); + } else if (str_eq(name.string, str_lit("run"))) { + AstNode *expr = parse_expr(f, false); + operand = make_run_expr(f, token, name, expr); + if (unparen_expr(expr)->kind != AstNode_CallExpr) { + error(ast_node_token(expr), "#run can only be applied to procedure calls"); + operand = make_bad_expr(f, token, f->curr_token); + } + warning(token, "#run is not yet implemented"); + } else { + operand = make_tag_expr(f, token, name, parse_expr(f, false)); + } + return operand; + } + + // Parse Procedure Type or Literal + case Token_proc: { + AstNode *curr_proc = f->curr_proc; + AstNode *type = parse_proc_type(f); + f->curr_proc = type; + + u64 tags = 0; + String foreign_name = {0}; + String link_name = {0}; + parse_proc_tags(f, &tags, &foreign_name, &link_name); + if (tags & ProcTag_foreign) { + syntax_error(f->curr_token, "#foreign cannot be applied to procedure literals"); + } + if (tags & ProcTag_link_name) { + syntax_error(f->curr_token, "#link_name cannot be applied to procedure literals"); + } + + if (f->curr_token.kind == Token_OpenBrace) { + AstNode *body; + + f->expr_level++; + body = parse_body(f); + f->expr_level--; + + type = make_proc_lit(f, type, body, tags); + } + f->curr_proc = curr_proc; + return type; + } + + default: { + AstNode *type = parse_identifier_or_type(f, 0); + if (type != NULL) { + // NOTE(bill): Sanity check as identifiers should be handled already + GB_ASSERT_MSG(type->kind != AstNode_Ident, "Type Cannot be identifier"); + return type; + } + } + } + + Token begin = f->curr_token; + syntax_error(begin, "Expected an operand"); + fix_advance_to_next_stmt(f); + return make_bad_expr(f, begin, f->curr_token); +} + +bool is_literal_type(AstNode *node) { + switch (node->kind) { + case AstNode_BadExpr: + case AstNode_Ident: + case AstNode_SelectorExpr: + case AstNode_ArrayType: + case AstNode_VectorType: + case AstNode_StructType: + return true; + } + return false; +} + +AstNode *parse_call_expr(AstFile *f, AstNode *operand) { + AstNodeArray args = make_ast_node_array(f); + Token open_paren, close_paren; + Token ellipsis = {0}; + + f->expr_level++; + open_paren = expect_token(f, Token_OpenParen); + + while (f->curr_token.kind != Token_CloseParen && + f->curr_token.kind != Token_EOF && + ellipsis.pos.line == 0) { + if (f->curr_token.kind == Token_Comma) + syntax_error(f->curr_token, "Expected an expression not a ,"); + + if (f->curr_token.kind == Token_Ellipsis) { + ellipsis = f->curr_token; + next_token(f); + } + + AstNode *arg = parse_expr(f, false); + array_add(&args, arg); + + if (f->curr_token.kind != Token_Comma) { + if (f->curr_token.kind == Token_CloseParen) + break; + } + + next_token(f); + } + + f->expr_level--; + close_paren = expect_token(f, Token_CloseParen); + + return make_call_expr(f, operand, args, open_paren, close_paren, ellipsis); +} + +AstNode *parse_atom_expr(AstFile *f, bool lhs) { + AstNode *operand = parse_operand(f, lhs); + + bool loop = true; + while (loop) { + switch (f->curr_token.kind) { + + case Token_Prime: { + Token op = expect_token(f, Token_Prime); + if (lhs) { + // TODO(bill): Handle this + } + AstNode *proc = parse_identifier(f); + AstNodeArray args; + array_init_reserve(&args, gb_arena_allocator(&f->arena), 1); + array_add(&args, operand); + operand = make_call_expr(f, proc, args, ast_node_token(operand), op, empty_token); + } break; + + case Token_OpenParen: { + if (lhs) { + // TODO(bill): Handle this shit! Is this even allowed in this language?! + } + operand = parse_call_expr(f, operand); + } break; + + case Token_Period: { + Token token = f->curr_token; + next_token(f); + if (lhs) { + // TODO(bill): handle this + } + switch (f->curr_token.kind) { + case Token_Identifier: + operand = make_selector_expr(f, token, operand, parse_identifier(f)); + break; + default: { + syntax_error(f->curr_token, "Expected a selector"); + next_token(f); + operand = make_selector_expr(f, f->curr_token, operand, NULL); + } break; + } + } break; + + case Token_OpenBracket: { + if (lhs) { + // TODO(bill): Handle this + } + Token open, close; + AstNode *indices[3] = {0}; + + f->expr_level++; + open = expect_token(f, Token_OpenBracket); + + if (f->curr_token.kind != Token_Colon) + indices[0] = parse_expr(f, false); + isize colon_count = 0; + Token colons[2] = {0}; + + while (f->curr_token.kind == Token_Colon && colon_count < 2) { + colons[colon_count++] = f->curr_token; + next_token(f); + if (f->curr_token.kind != Token_Colon && + f->curr_token.kind != Token_CloseBracket && + f->curr_token.kind != Token_EOF) { + indices[colon_count] = parse_expr(f, false); + } + } + + f->expr_level--; + close = expect_token(f, Token_CloseBracket); + + if (colon_count == 0) { + operand = make_index_expr(f, operand, indices[0], open, close); + } else { + bool triple_indexed = false; + if (colon_count == 2) { + triple_indexed = true; + if (indices[1] == NULL) { + syntax_error(colons[0], "Second index is required in a triple indexed slice"); + indices[1] = make_bad_expr(f, colons[0], colons[1]); + } + if (indices[2] == NULL) { + syntax_error(colons[1], "Third index is required in a triple indexed slice"); + indices[2] = make_bad_expr(f, colons[1], close); + } + } + operand = make_slice_expr(f, operand, open, close, indices[0], indices[1], indices[2], triple_indexed); + } + } break; + + case Token_Pointer: // Deference + operand = make_deref_expr(f, operand, expect_token(f, Token_Pointer)); + break; + + case Token_Maybe: // Demaybe + operand = make_demaybe_expr(f, operand, expect_token(f, Token_Maybe)); + break; + + case Token_OpenBrace: { + if (!lhs && is_literal_type(operand) && f->expr_level >= 0) { + if (f->curr_token.pos.line == f->prev_token.pos.line) { + // TODO(bill): This is a hack due to optional semicolons + // TODO(bill): It's probably much better to solve this by changing + // the syntax for struct literals and array literals + operand = parse_literal_value(f, operand); + } else { + loop = false; + } + } else { + loop = false; + } + } break; + + default: + loop = false; + break; + } + + lhs = false; // NOTE(bill): 'tis not lhs anymore + } + + return operand; +} + +AstNode *parse_type(AstFile *f); + +AstNode *parse_unary_expr(AstFile *f, bool lhs) { + switch (f->curr_token.kind) { + case Token_Pointer: + case Token_Maybe: + case Token_Add: + case Token_Sub: + case Token_Not: + case Token_Xor: { + AstNode *operand; + Token op = f->curr_token; + next_token(f); + operand = parse_unary_expr(f, lhs); + return make_unary_expr(f, op, operand); + } break; + } + + return parse_atom_expr(f, lhs); +} + +// NOTE(bill): result == priority +i32 token_precedence(Token t) { + switch (t.kind) { + case Token_CmpOr: + return 1; + case Token_CmpAnd: + return 2; + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_Gt: + case Token_LtEq: + case Token_GtEq: + return 3; + case Token_Add: + case Token_Sub: + case Token_Or: + case Token_Xor: + return 4; + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_AndNot: + case Token_Shl: + case Token_Shr: + return 5; + case Token_DoublePrime: + return 6; + case Token_as: + case Token_transmute: + case Token_down_cast: + case Token_union_cast: + return 7; + } + + return 0; +} + +AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { + AstNode *expression = parse_unary_expr(f, lhs); + for (i32 prec = token_precedence(f->curr_token); prec >= prec_in; prec--) { + for (;;) { + AstNode *right; + Token op = f->curr_token; + i32 op_prec = token_precedence(op); + if (op_prec != prec) + break; + expect_operator(f); // NOTE(bill): error checks too + if (lhs) { + // TODO(bill): error checking + lhs = false; + } + + switch (op.kind) { + case Token_DoublePrime: { + // TODO(bill): Properly define semantic for in-fix and post-fix calls + AstNode *proc = parse_identifier(f); + /* if (f->curr_token.kind == Token_OpenParen) { + AstNode *call = parse_call_expr(f, proc); + array_add(&call->CallExpr.args, expression); + for (isize i = gb_array_count(call->CallExpr.args)-1; i > 0; i--) { + gb_swap(AstNode *, call->CallExpr.args[i], call->CallExpr.args[i-1]); + } + + expression = call; + } else */{ + right = parse_binary_expr(f, false, prec+1); + AstNodeArray args = {0}; + array_init_reserve(&args, gb_arena_allocator(&f->arena), 2); + array_add(&args, expression); + array_add(&args, right); + expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token); + } + continue; + } break; + + case Token_as: + case Token_transmute: + case Token_down_cast: + case Token_union_cast: + right = parse_type(f); + break; + + default: + right = parse_binary_expr(f, false, prec+1); + if (!right) { + syntax_error(op, "Expected expression on the right hand side of the binary operator"); + } + break; + } + expression = make_binary_expr(f, op, expression, right); + } + } + return expression; +} + +AstNode *parse_expr(AstFile *f, bool lhs) { + return parse_binary_expr(f, lhs, 0+1); +} + + +AstNodeArray parse_expr_list(AstFile *f, bool lhs) { + AstNodeArray list = make_ast_node_array(f); + do { + AstNode *e = parse_expr(f, lhs); + array_add(&list, e); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } while (true); + + return list; +} + +AstNodeArray parse_lhs_expr_list(AstFile *f) { + return parse_expr_list(f, true); +} + +AstNodeArray parse_rhs_expr_list(AstFile *f) { + return parse_expr_list(f, false); +} + +AstNode *parse_decl(AstFile *f, AstNodeArray names); + +AstNode *parse_simple_stmt(AstFile *f) { + isize lhs_count = 0, rhs_count = 0; + AstNodeArray lhs = parse_lhs_expr_list(f); + + + AstNode *statement = NULL; + Token token = f->curr_token; + switch (token.kind) { + case Token_Eq: + case Token_AddEq: + case Token_SubEq: + case Token_MulEq: + case Token_QuoEq: + case Token_ModEq: + case Token_AndEq: + case Token_OrEq: + case Token_XorEq: + case Token_ShlEq: + case Token_ShrEq: + case Token_AndNotEq: + case Token_CmpAndEq: + case Token_CmpOrEq: + { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a simple statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + next_token(f); + AstNodeArray rhs = parse_rhs_expr_list(f); + if (rhs.count == 0) { + syntax_error(token, "No right-hand side in assignment statement."); + return make_bad_stmt(f, token, f->curr_token); + } + return make_assign_stmt(f, token, lhs, rhs); + } break; + + case Token_Colon: // Declare + return parse_decl(f, lhs); + } + + if (lhs_count > 1) { + syntax_error(token, "Expected 1 expression"); + return make_bad_stmt(f, token, f->curr_token); + } + + token = f->curr_token; + switch (token.kind) { + case Token_Increment: + case Token_Decrement: + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a simple statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + statement = make_inc_dec_stmt(f, token, lhs.e[0]); + next_token(f); + return statement; + } + + return make_expr_stmt(f, lhs.e[0]); +} + + + +AstNode *parse_block_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a block statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + AstNode *block_stmt = parse_body(f); + return block_stmt; +} + +AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { + if (statement == NULL) + return NULL; + + if (statement->kind == AstNode_ExprStmt) + return statement->ExprStmt.expr; + + syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind)); + return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); +} + +AstNodeArray parse_identfier_list(AstFile *f) { + AstNodeArray list = make_ast_node_array(f); + + do { + array_add(&list, parse_identifier(f)); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } while (true); + + return list; +} + + + +AstNode *parse_type_attempt(AstFile *f) { + AstNode *type = parse_identifier_or_type(f, 0); + if (type != NULL) { + // TODO(bill): Handle? + } + return type; +} + +AstNode *parse_type(AstFile *f) { + AstNode *type = parse_type_attempt(f); + if (type == NULL) { + Token token = f->curr_token; + syntax_error(token, "Expected a type"); + next_token(f); + return make_bad_expr(f, token, f->curr_token); + } + return type; +} + + +Token parse_procedure_signature(AstFile *f, + AstNodeArray *params, AstNodeArray *results); + +AstNode *parse_proc_type(AstFile *f) { + AstNodeArray params = {0}; + AstNodeArray results = {0}; + + Token proc_token = parse_procedure_signature(f, ¶ms, &results); + + return make_proc_type(f, proc_token, params, results); +} + + +AstNodeArray parse_parameter_list(AstFile *f) { + AstNodeArray params = make_ast_node_array(f); + + while (f->curr_token.kind == Token_Identifier || + f->curr_token.kind == Token_using) { + bool is_using = false; + if (allow_token(f, Token_using)) { + is_using = true; + } + + AstNodeArray names = parse_lhs_expr_list(f); + if (names.count == 0) { + syntax_error(f->curr_token, "Empty parameter declaration"); + } + + if (names.count > 1 && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); + is_using = false; + } + + expect_token_after(f, Token_Colon, "parameter list"); + + AstNode *type = NULL; + if (f->curr_token.kind == Token_Ellipsis) { + Token ellipsis = f->curr_token; + next_token(f); + type = parse_type_attempt(f); + if (type == NULL) { + syntax_error(f->curr_token, "variadic parameter is missing a type after `..`"); + type = make_bad_expr(f, ellipsis, f->curr_token); + } else { + if (names.count > 1) { + syntax_error(f->curr_token, "mutliple variadic parameters, only `..`"); + } else { + type = make_ellipsis(f, ellipsis, type); + } + } + } else { + type = parse_type_attempt(f); + } + + + if (type == NULL) { + syntax_error(f->curr_token, "Expected a type for this parameter declaration"); + } + + array_add(¶ms, make_parameter(f, names, type, is_using)); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + return params; +} + + +AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, bool using_allowed) { + AstNodeArray decls = make_ast_node_array(f); + isize decl_count = 0; + + while (f->curr_token.kind == Token_Identifier || + f->curr_token.kind == Token_using) { + bool is_using = false; + if (allow_token(f, Token_using)) { + is_using = true; + } + AstNodeArray names = parse_lhs_expr_list(f); + if (names.count == 0) { + syntax_error(f->curr_token, "Empty field declaration"); + } + + if (!using_allowed && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to members of a union"); + is_using = false; + } + if (names.count > 1 && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); + } + + AstNode *decl = NULL; + + if (f->curr_token.kind == Token_Colon) { + decl = parse_decl(f, names); + + if (decl->kind == AstNode_ProcDecl) { + syntax_error(f->curr_token, "Procedure declarations are not allowed within a structure"); + decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); + } + } else { + syntax_error(f->curr_token, "Illegal structure field"); + decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); + } + + expect_semicolon_after_stmt(f, decl); + + if (is_ast_node_decl(decl)) { + array_add(&decls, decl); + if (decl->kind == AstNode_VarDecl) { + decl->VarDecl.is_using = is_using && using_allowed; + if (decl->VarDecl.values.count > 0) { + syntax_error(f->curr_token, "Default variable assignments within a structure will be ignored (at the moment)"); + } + } else { + decl_count += 1; + } + } + } + + if (decl_count_) *decl_count_ = decl_count; + + return decls; +} + +AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { + switch (f->curr_token.kind) { + case Token_Identifier: { + AstNode *e = parse_identifier(f); + while (f->curr_token.kind == Token_Period) { + Token token = f->curr_token; + next_token(f); + AstNode *sel = parse_identifier(f); + e = make_selector_expr(f, token, e, sel); + } + if (f->curr_token.kind == Token_OpenParen) { + // HACK NOTE(bill): For type_of_val(expr) + e = parse_call_expr(f, e); + } + return e; + } + + case Token_Pointer: { + Token token = expect_token(f, Token_Pointer); + AstNode *elem = parse_type(f); + return make_pointer_type(f, token, elem); + } + + case Token_Maybe: { + Token token = expect_token(f, Token_Maybe); + AstNode *elem = parse_type(f); + return make_maybe_type(f, token, elem); + } + + case Token_OpenBracket: { + f->expr_level++; + Token token = expect_token(f, Token_OpenBracket); + AstNode *count_expr = NULL; + + if (f->curr_token.kind == Token_Ellipsis) { + count_expr = make_ellipsis(f, f->curr_token, NULL); + next_token(f); + } else if (f->curr_token.kind != Token_CloseBracket) { + count_expr = parse_expr(f, false); + } + expect_token(f, Token_CloseBracket); + f->expr_level--; + AstNode *e = make_array_type(f, token, count_expr, parse_type(f)); + return e; + } + + case Token_OpenBrace: { + f->expr_level++; + Token token = expect_token(f, Token_OpenBrace); + AstNode *count_expr = parse_expr(f, false); + expect_token(f, Token_CloseBrace); + f->expr_level--; + return make_vector_type(f, token, count_expr, parse_type(f)); + } + + case Token_struct: { + Token token = expect_token(f, Token_struct); + bool is_packed = false; + bool is_ordered = false; + while (allow_token(f, Token_Hash)) { + Token tag = expect_token_after(f, Token_Identifier, "`#`"); + if (str_eq(tag.string, str_lit("packed"))) { + if (is_packed) { + syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string)); + } + is_packed = true; + } else if (str_eq(tag.string, str_lit("ordered"))) { + if (is_ordered) { + syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string)); + } + is_ordered = true; + } else { + syntax_error(tag, "Invalid struct tag `#%.*s`", LIT(tag.string)); + } + } + + if (is_packed && is_ordered) { + syntax_error(token, "`#ordered` is not needed with `#packed` which implies ordering"); + } + + Token open = expect_token_after(f, Token_OpenBrace, "`struct`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, true); + Token close = expect_token(f, Token_CloseBrace); + + return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered); + } break; + + case Token_union: { + Token token = expect_token(f, Token_union); + Token open = expect_token_after(f, Token_OpenBrace, "`union`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, false); + Token close = expect_token(f, Token_CloseBrace); + + return make_union_type(f, token, decls, decl_count); + } + + case Token_raw_union: { + Token token = expect_token(f, Token_raw_union); + Token open = expect_token_after(f, Token_OpenBrace, "`raw_union`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, true); + Token close = expect_token(f, Token_CloseBrace); + + return make_raw_union_type(f, token, decls, decl_count); + } + + case Token_enum: { + Token token = expect_token(f, Token_enum); + AstNode *base_type = NULL; + Token open, close; + + if (f->curr_token.kind != Token_OpenBrace) { + base_type = parse_type(f); + } + + AstNodeArray fields = make_ast_node_array(f); + + open = expect_token_after(f, Token_OpenBrace, "`enum`"); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *name = parse_identifier(f); + AstNode *value = NULL; + Token eq = empty_token; + if (f->curr_token.kind == Token_Eq) { + eq = expect_token(f, Token_Eq); + value = parse_value(f); + } + AstNode *field = make_field_value(f, name, value, eq); + array_add(&fields, field); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + close = expect_token(f, Token_CloseBrace); + + return make_enum_type(f, token, base_type, fields); + } + + case Token_proc: + return parse_proc_type(f); + + case Token_OpenParen: { + // NOTE(bill): Skip the paren expression + AstNode *type; + Token open, close; + open = expect_token(f, Token_OpenParen); + type = parse_type(f); + close = expect_token(f, Token_CloseParen); + return type; + // return make_paren_expr(f, type, open, close); + } + + // TODO(bill): Why is this even allowed? Is this a parsing error? + case Token_Colon: + break; + + case Token_Eq: + if (f->prev_token.kind == Token_Colon) + break; + // fallthrough + default: + syntax_error(f->curr_token, + "Expected a type or identifier after `%.*s`, got `%.*s`", LIT(f->prev_token.string), LIT(f->curr_token.string)); + break; + } + + return NULL; +} + + +AstNodeArray parse_results(AstFile *f) { + AstNodeArray results = make_ast_node_array(f); + if (allow_token(f, Token_ArrowRight)) { + if (f->curr_token.kind == Token_OpenParen) { + expect_token(f, Token_OpenParen); + while (f->curr_token.kind != Token_CloseParen && + f->curr_token.kind != Token_EOF) { + array_add(&results, parse_type(f)); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + expect_token(f, Token_CloseParen); + + return results; + } + + array_add(&results, parse_type(f)); + return results; + } + return results; +} + +Token parse_procedure_signature(AstFile *f, + AstNodeArray *params, + AstNodeArray *results) { + Token proc_token = expect_token(f, Token_proc); + expect_token(f, Token_OpenParen); + *params = parse_parameter_list(f); + expect_token_after(f, Token_CloseParen, "parameter list"); + *results = parse_results(f); + return proc_token; +} + +AstNode *parse_body(AstFile *f) { + AstNodeArray stmts = {0}; + Token open, close; + open = expect_token(f, Token_OpenBrace); + stmts = parse_stmt_list(f); + close = expect_token(f, Token_CloseBrace); + + return make_block_stmt(f, stmts, open, close); +} + + + +AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) { + AstNodeArray params = {0}; + AstNodeArray results = {0}; + + parse_procedure_signature(f, ¶ms, &results); + AstNode *proc_type = make_proc_type(f, proc_token, params, results); + + AstNode *body = NULL; + u64 tags = 0; + String foreign_name = {0}; + String link_name = {0}; + + parse_proc_tags(f, &tags, &foreign_name, &link_name); + + AstNode *curr_proc = f->curr_proc; + f->curr_proc = proc_type; + + if (f->curr_token.kind == Token_OpenBrace) { + if ((tags & ProcTag_foreign) != 0) { + syntax_error(f->curr_token, "A procedure tagged as `#foreign` cannot have a body"); + } + body = parse_body(f); + } + + f->curr_proc = curr_proc; + return make_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name); +} + +AstNode *parse_decl(AstFile *f, AstNodeArray names) { + AstNodeArray values = {0}; + AstNode *type = NULL; + + for_array(i, names) { + AstNode *name = names.e[i]; + if (name->kind == AstNode_Ident) { + String n = name->Ident.string; + // NOTE(bill): Check for reserved identifiers + if (str_eq(n, str_lit("context"))) { + syntax_error(ast_node_token(name), "`context` is a reserved identifier"); + break; + } + } + } + + if (allow_token(f, Token_Colon)) { + if (!allow_token(f, Token_type)) { + type = parse_identifier_or_type(f, 0); + } + } else if (f->curr_token.kind != Token_Eq && f->curr_token.kind != Token_Semicolon) { + syntax_error(f->curr_token, "Expected type separator `:` or `=`"); + } + + bool is_mutable = true; + + if (f->curr_token.kind == Token_Eq || + f->curr_token.kind == Token_Colon) { + if (f->curr_token.kind == Token_Colon) { + is_mutable = false; + } + next_token(f); + + if (f->curr_token.kind == Token_type || + f->curr_token.kind == Token_struct || + f->curr_token.kind == Token_enum || + f->curr_token.kind == Token_union || + f->curr_token.kind == Token_raw_union) { + Token token = f->curr_token; + if (token.kind == Token_type) { + next_token(f); + } + if (names.count != 1) { + syntax_error(ast_node_token(names.e[0]), "You can only declare one type at a time"); + return make_bad_decl(f, names.e[0]->Ident, token); + } + + if (type != NULL) { + syntax_error(f->prev_token, "Expected either `type` or nothing between : and :"); + // NOTE(bill): Do not fail though + } + + return make_type_decl(f, token, names.e[0], parse_type(f)); + } else if (f->curr_token.kind == Token_proc && + is_mutable == false) { + // NOTE(bill): Procedure declarations + Token proc_token = f->curr_token; + AstNode *name = names.e[0]; + if (names.count != 1) { + syntax_error(proc_token, "You can only declare one procedure at a time"); + return make_bad_decl(f, name->Ident, proc_token); + } + + return parse_proc_decl(f, proc_token, name); + + } else { + values = parse_rhs_expr_list(f); + if (values.count > names.count) { + syntax_error(f->curr_token, "Too many values on the right hand side of the declaration"); + } else if (values.count < names.count && !is_mutable) { + syntax_error(f->curr_token, "All constant declarations must be defined"); + } else if (values.count == 0) { + syntax_error(f->curr_token, "Expected an expression for this declaration"); + } + } + } + + if (is_mutable) { + if (type == NULL && values.count == 0) { + syntax_error(f->curr_token, "Missing variable type or initialization"); + return make_bad_decl(f, f->curr_token, f->curr_token); + } + } else { + if (type == NULL && values.count == 0 && names.count > 0) { + syntax_error(f->curr_token, "Missing constant value"); + return make_bad_decl(f, f->curr_token, f->curr_token); + } + } + + if (values.e == NULL) { + values = make_ast_node_array(f); + } + + if (is_mutable) { + return make_var_decl(f, names, type, values); + } + return make_const_decl(f, names, type, values); +} + + +AstNode *parse_if_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use an if statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_if); + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *body = NULL; + AstNode *else_stmt = NULL; + + isize prev_level = f->expr_level; + f->expr_level = -1; + + + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + init = parse_simple_stmt(f); + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); + init = NULL; + } + } + + f->expr_level = prev_level; + + if (cond == NULL) { + syntax_error(f->curr_token, "Expected condition for if statement"); + } + + body = parse_block_stmt(f); + + if (allow_token(f, Token_else)) { + switch (f->curr_token.kind) { + case Token_if: + else_stmt = parse_if_stmt(f); + break; + case Token_OpenBrace: + else_stmt = parse_block_stmt(f); + break; + default: + syntax_error(f->curr_token, "Expected if statement block statement"); + else_stmt = make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); + break; + } + } + + return make_if_stmt(f, token, init, cond, body, else_stmt); +} + +AstNode *parse_return_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a return statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_return); + AstNodeArray results = make_ast_node_array(f); + + if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace && + f->curr_token.pos.line == token.pos.line) { + results = parse_rhs_expr_list(f); + } + if (f->curr_token.kind != Token_CloseBrace) { + expect_semicolon_after_stmt(f, results.e[0]); + } + + return make_return_stmt(f, token, results); +} + +AstNode *parse_for_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a for statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_for); + + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *end = NULL; + AstNode *body = NULL; + + if (f->curr_token.kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f); + if (is_ast_node_complex_stmt(cond)) { + syntax_error(f->curr_token, + "You are not allowed that type of statement in a for statement, it is too complex!"); + } + } + + if (allow_token(f, Token_Semicolon)) { + init = cond; + cond = NULL; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f); + } + expect_token(f, Token_Semicolon); + if (f->curr_token.kind != Token_OpenBrace) { + end = parse_simple_stmt(f); + } + } + f->expr_level = prev_level; + } + body = parse_block_stmt(f); + + cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); + + return make_for_stmt(f, token, init, cond, end, body); +} + +AstNode *parse_case_clause(AstFile *f) { + Token token = f->curr_token; + AstNodeArray list = make_ast_node_array(f); + if (allow_token(f, Token_case)) { + list = parse_rhs_expr_list(f); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? + AstNodeArray stmts = parse_stmt_list(f); + + return make_case_clause(f, token, list, stmts); +} + + +AstNode *parse_type_case_clause(AstFile *f) { + Token token = f->curr_token; + AstNodeArray clause = make_ast_node_array(f); + if (allow_token(f, Token_case)) { + array_add(&clause, parse_type(f)); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? + AstNodeArray stmts = parse_stmt_list(f); + + return make_case_clause(f, token, clause, stmts); +} + + +AstNode *parse_match_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a match statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_match); + AstNode *init = NULL; + AstNode *tag = NULL; + AstNode *body = NULL; + Token open, close; + + if (allow_token(f, Token_type)) { + isize prev_level = f->expr_level; + f->expr_level = -1; + + AstNode *var = parse_identifier(f); + expect_token(f, Token_Colon); + tag = parse_simple_stmt(f); + + f->expr_level = prev_level; + + open = expect_token(f, Token_OpenBrace); + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind == Token_case || + f->curr_token.kind == Token_default) { + array_add(&list, parse_type_case_clause(f)); + } + + close = expect_token(f, Token_CloseBrace); + body = make_block_stmt(f, list, open, close); + + tag = convert_stmt_to_expr(f, tag, str_lit("type match expression")); + return make_type_match_stmt(f, token, tag, var, body); + } else { + if (f->curr_token.kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->curr_token.kind != Token_Semicolon) { + tag = parse_simple_stmt(f); + } + if (allow_token(f, Token_Semicolon)) { + init = tag; + tag = NULL; + if (f->curr_token.kind != Token_OpenBrace) { + tag = parse_simple_stmt(f); + } + } + + f->expr_level = prev_level; + } + + open = expect_token(f, Token_OpenBrace); + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind == Token_case || + f->curr_token.kind == Token_default) { + array_add(&list, parse_case_clause(f)); + } + + close = expect_token(f, Token_CloseBrace); + + body = make_block_stmt(f, list, open, close); + + tag = convert_stmt_to_expr(f, tag, str_lit("match expression")); + return make_match_stmt(f, token, init, tag, body); + } +} + + +AstNode *parse_defer_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a defer statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_defer); + AstNode *statement = parse_stmt(f); + switch (statement->kind) { + case AstNode_EmptyStmt: + syntax_error(token, "Empty statement after defer (e.g. `;`)"); + break; + case AstNode_DeferStmt: + syntax_error(token, "You cannot defer a defer statement"); + break; + case AstNode_ReturnStmt: + syntax_error(token, "You cannot a return statement"); + break; + } + + return make_defer_stmt(f, token, statement); +} + +AstNode *parse_asm_stmt(AstFile *f) { + Token token = expect_token(f, Token_asm); + bool is_volatile = false; + if (allow_token(f, Token_volatile)) { + is_volatile = true; + } + Token open, close, code_string; + open = expect_token(f, Token_OpenBrace); + code_string = expect_token(f, Token_String); + AstNode *output_list = NULL; + AstNode *input_list = NULL; + AstNode *clobber_list = NULL; + isize output_count = 0; + isize input_count = 0; + isize clobber_count = 0; + + // TODO(bill): Finish asm statement and determine syntax + + // if (f->curr_token.kind != Token_CloseBrace) { + // expect_token(f, Token_Colon); + // } + + close = expect_token(f, Token_CloseBrace); + + return make_asm_stmt(f, token, is_volatile, open, close, code_string, + output_list, input_list, clobber_list, + output_count, input_count, clobber_count); + +} + + + +AstNode *parse_stmt(AstFile *f) { + AstNode *s = NULL; + Token token = f->curr_token; + switch (token.kind) { + case Token_Comment: + next_token(f); + return parse_stmt(f); + + // Operands + case Token_Identifier: + case Token_Integer: + case Token_Float: + case Token_Rune: + case Token_String: + case Token_OpenParen: + case Token_proc: + // Unary Operators + case Token_Add: + case Token_Sub: + case Token_Xor: + case Token_Not: + s = parse_simple_stmt(f); + expect_semicolon_after_stmt(f, s); + return s; + + // TODO(bill): other keywords + case Token_if: return parse_if_stmt(f); + case Token_return: return parse_return_stmt(f); + case Token_for: return parse_for_stmt(f); + case Token_match: return parse_match_stmt(f); + case Token_defer: return parse_defer_stmt(f); + case Token_asm: return parse_asm_stmt(f); + + case Token_break: + case Token_continue: + case Token_fallthrough: + next_token(f); + s = make_branch_stmt(f, token); + expect_semicolon_after_stmt(f, s); + return s; + + + case Token_using: { + AstNode *node = NULL; + + next_token(f); + node = parse_stmt(f); + + bool valid = false; + + switch (node->kind) { + case AstNode_ExprStmt: { + AstNode *e = unparen_expr(node->ExprStmt.expr); + while (e->kind == AstNode_SelectorExpr) { + e = unparen_expr(e->SelectorExpr.selector); + } + if (e->kind == AstNode_Ident) { + valid = true; + } + } break; + case AstNode_VarDecl: + valid = true; + break; + } + + if (!valid) { + syntax_error(token, "Illegal use of `using` statement."); + return make_bad_stmt(f, token, f->curr_token); + } + + + return make_using_stmt(f, token, node); + } break; + + case Token_push_allocator: { + next_token(f); + isize prev_level = f->expr_level; + f->expr_level = -1; + AstNode *expr = parse_expr(f, false); + f->expr_level = prev_level; + + AstNode *body = parse_block_stmt(f); + return make_push_allocator(f, token, expr, body); + } break; + + case Token_push_context: { + next_token(f); + isize prev_level = f->expr_level; + f->expr_level = -1; + AstNode *expr = parse_expr(f, false); + f->expr_level = prev_level; + + AstNode *body = parse_block_stmt(f); + return make_push_context(f, token, expr, body); + } break; + + case Token_Hash: { + s = parse_tag_stmt(f, NULL); + String tag = s->TagStmt.name.string; + if (str_eq(tag, str_lit("shared_global_scope"))) { + if (f->curr_proc == NULL) { + f->is_global_scope = true; + return make_empty_stmt(f, f->curr_token); + } + syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, f->curr_token); + } else if (str_eq(tag, str_lit("import"))) { + // TODO(bill): better error messages + Token import_name = {0}; + Token file_path = expect_token_after(f, Token_String, "#import"); + if (allow_token(f, Token_as)) { + // NOTE(bill): Custom import name + if (f->curr_token.kind == Token_Period) { + import_name = f->curr_token; + import_name.kind = Token_Identifier; + next_token(f); + } else { + import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration"); + } + + if (str_eq(import_name.string, str_lit("_"))) { + syntax_error(token, "Illegal import name: `_`"); + return make_bad_decl(f, token, f->curr_token); + } + } + + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path, import_name, false); + } + syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("load"))) { + // TODO(bill): better error messages + Token file_path = expect_token(f, Token_String); + Token import_name = file_path; + import_name.string = str_lit("."); + + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path, import_name, true); + } + syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("foreign_system_library"))) { + Token file_path = expect_token(f, Token_String); + if (f->curr_proc == NULL) { + return make_foreign_library(f, s->TagStmt.token, file_path, true); + } + syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("foreign_library"))) { + Token file_path = expect_token(f, Token_String); + if (f->curr_proc == NULL) { + return make_foreign_library(f, s->TagStmt.token, file_path, false); + } + syntax_error(token, "You cannot use #foreign_library within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("thread_local"))) { + AstNode *var_decl = parse_simple_stmt(f); + if (var_decl->kind != AstNode_VarDecl) { + syntax_error(token, "#thread_local may only be applied to variable declarations"); + return make_bad_decl(f, token, ast_node_token(var_decl)); + } + if (f->curr_proc != NULL) { + syntax_error(token, "#thread_local is only allowed at the file scope"); + return make_bad_decl(f, token, ast_node_token(var_decl)); + } + var_decl->VarDecl.tags |= VarDeclTag_thread_local; + return var_decl; + } else if (str_eq(tag, str_lit("bounds_check"))) { + s = parse_stmt(f); + s->stmt_state_flags |= StmtStateFlag_bounds_check; + if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); + } + return s; + } else if (str_eq(tag, str_lit("no_bounds_check"))) { + s = parse_stmt(f); + s->stmt_state_flags |= StmtStateFlag_no_bounds_check; + if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) { + syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); + } + return s; + } + + s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument + return s; + } break; + + case Token_OpenBrace: + return parse_block_stmt(f); + + case Token_Semicolon: + s = make_empty_stmt(f, token); + next_token(f); + return s; + } + + syntax_error(token, + "Expected a statement, got `%.*s`", + LIT(token_strings[token.kind])); + fix_advance_to_next_stmt(f); + return make_bad_stmt(f, token, f->curr_token); +} + +AstNodeArray parse_stmt_list(AstFile *f) { + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind != Token_case && + f->curr_token.kind != Token_default && + f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *stmt = parse_stmt(f); + if (stmt && stmt->kind != AstNode_EmptyStmt) { + array_add(&list, stmt); + } + } + + return list; +} + + +ParseFileError init_ast_file(AstFile *f, String fullpath) { + if (!string_has_extension(fullpath, str_lit("odin"))) { + return ParseFile_WrongExtension; + } + TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath); + if (err == TokenizerInit_None) { + array_init(&f->tokens, heap_allocator()); + { + for (;;) { + Token token = tokenizer_get_token(&f->tokenizer); + if (token.kind == Token_Invalid) { + return ParseFile_InvalidToken; + } + if (token.kind == Token_Comment) { + continue; + } + array_add(&f->tokens, token); + + if (token.kind == Token_EOF) { + break; + } + } + } + + f->curr_token_index = 0; + f->prev_token = f->tokens.e[f->curr_token_index]; + f->curr_token = f->tokens.e[f->curr_token_index]; + + // NOTE(bill): Is this big enough or too small? + isize arena_size = gb_size_of(AstNode); + arena_size *= 2*f->tokens.count; + gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size); + + f->curr_proc = NULL; + + return ParseFile_None; + } + + switch (err) { + case TokenizerInit_NotExists: + return ParseFile_NotFound; + case TokenizerInit_Permission: + return ParseFile_Permission; + case TokenizerInit_Empty: + return ParseFile_EmptyFile; + } + + return ParseFile_InvalidFile; +} + +void destroy_ast_file(AstFile *f) { + gb_arena_free(&f->arena); + array_free(&f->tokens); + gb_free(heap_allocator(), f->tokenizer.fullpath.text); + destroy_tokenizer(&f->tokenizer); +} + +bool init_parser(Parser *p) { + array_init(&p->files, heap_allocator()); + array_init(&p->imports, heap_allocator()); + array_init(&p->foreign_libraries, heap_allocator()); + gb_mutex_init(&p->mutex); + return true; +} + +void destroy_parser(Parser *p) { + // TODO(bill): Fix memory leak + for_array(i, p->files) { + destroy_ast_file(&p->files.e[i]); + } +#if 1 + for_array(i, p->imports) { + // gb_free(heap_allocator(), p->imports[i].text); + } +#endif + array_free(&p->files); + array_free(&p->imports); + array_free(&p->foreign_libraries); + gb_mutex_destroy(&p->mutex); +} + +// NOTE(bill): Returns true if it's added +bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) { + gb_mutex_lock(&p->mutex); + + for_array(i, p->imports) { + String import = p->imports.e[i].path; + if (str_eq(import, path)) { + return false; + } + } + + ImportedFile item; + item.path = path; + item.rel_path = rel_path; + item.pos = pos; + array_add(&p->imports, item); + + gb_mutex_unlock(&p->mutex); + + return true; +} + +String get_fullpath_relative(gbAllocator a, String base_dir, String path) { + String res = {0}; + isize str_len = base_dir.len+path.len; + + u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); + + isize i = 0; + 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'; + res = path_to_fullpath(a, make_string(str, str_len)); + gb_free(heap_allocator(), str); + return res; +} + +String get_fullpath_core(gbAllocator a, String path) { + String module_dir = get_module_dir(); + String res = {0}; + + char core[] = "core/"; + isize core_len = gb_size_of(core)-1; + + isize str_len = module_dir.len + core_len + path.len; + u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); + + 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'; + + res = path_to_fullpath(a, make_string(str, str_len)); + gb_free(heap_allocator(), str); + return res; +} + +// NOTE(bill): Returns true if it's added +bool try_add_foreign_library_path(Parser *p, String import_file) { + gb_mutex_lock(&p->mutex); + + for_array(i, p->foreign_libraries) { + String import = p->foreign_libraries.e[i]; + if (str_eq(import, import_file)) { + return false; + } + } + array_add(&p->foreign_libraries, import_file); + gb_mutex_unlock(&p->mutex); + return true; +} + +gb_global Rune illegal_import_runes[] = { + '"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f', + '\\', // NOTE(bill): Disallow windows style filepaths + '!', '$', '%', '^', '&', '*', '(', ')', '=', '+', + '[', ']', '{', '}', + ';', ':', '#', + '|', ',', '<', '>', '?', +}; + +bool is_import_path_valid(String path) { + if (path.len > 0) { + u8 *start = path.text; + u8 *end = path.text + path.len; + u8 *curr = start; + Rune r = -1; + while (curr < end) { + isize width = 1; + r = curr[0]; + if (r >= 0x80) { + width = gb_utf8_decode(curr, end-curr, &r); + if (r == GB_RUNE_INVALID && width == 1) + return false; + else if (r == GB_RUNE_BOM && curr-start > 0) + return false; + } + + for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) { + if (r == illegal_import_runes[i]) + return false; + } + + curr += width; + } + + return true; + } + return false; +} + +String get_filepath_extension(String path) { + isize dot = 0; + bool seen_slash = false; + for (isize i = path.len-1; i >= 0; i--) { + u8 c = path.text[i]; + if (c == '/' || c == '\\') { + seen_slash = true; + } + + if (c == '.') { + if (seen_slash) { + return str_lit(""); + } + + dot = i; + break; + } + } + return make_string(path.text, dot); +} + +void parse_file(Parser *p, AstFile *f) { + String filepath = f->tokenizer.fullpath; + String base_dir = filepath; + for (isize i = filepath.len-1; i >= 0; i--) { + if (base_dir.text[i] == '\\' || + base_dir.text[i] == '/') { + break; + } + base_dir.len--; + } + + + f->decls = parse_stmt_list(f); + + for_array(i, f->decls) { + AstNode *node = f->decls.e[i]; + if (!is_ast_node_decl(node) && + node->kind != AstNode_BadStmt && + node->kind != AstNode_EmptyStmt) { + // NOTE(bill): Sanity check + syntax_error(ast_node_token(node), "Only declarations are allowed at file scope"); + } else { + if (node->kind == AstNode_ImportDecl) { + AstNodeImportDecl *id = &node->ImportDecl; + String file_str = id->relpath.string; + + if (!is_import_path_valid(file_str)) { + if (id->is_load) { + syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str)); + } else { + syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str)); + } + // NOTE(bill): It's a naughty name + f->decls.e[i] = make_bad_decl(f, id->token, id->token); + continue; + } + + gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator + + String rel_path = get_fullpath_relative(allocator, base_dir, file_str); + String import_file = rel_path; + if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated + String abs_path = get_fullpath_core(allocator, file_str); + if (gb_file_exists(cast(char *)abs_path.text)) { + import_file = abs_path; + } + } + + id->fullpath = import_file; + try_add_import_path(p, import_file, file_str, ast_node_token(node).pos); + + } else if (node->kind == AstNode_ForeignLibrary) { + AstNodeForeignLibrary *id = &node->ForeignLibrary; + String file_str = id->filepath.string; + + if (!is_import_path_valid(file_str)) { + if (id->is_system) { + syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path"); + } else { + syntax_error(ast_node_token(node), "Invalid `foreign_library` path"); + } + // NOTE(bill): It's a naughty name + f->decls.e[i] = make_bad_decl(f, id->token, id->token); + continue; + } + + if (!id->is_system) { + gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator + + String rel_path = get_fullpath_relative(allocator, base_dir, file_str); + String import_file = rel_path; + if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated + String abs_path = get_fullpath_core(allocator, file_str); + if (gb_file_exists(cast(char *)abs_path.text)) { + import_file = abs_path; + } + } + file_str = import_file; + } + + try_add_foreign_library_path(p, file_str); + } + } + } +} + + + +ParseFileError parse_files(Parser *p, char *init_filename) { + char *fullpath_str = gb_path_get_full_name(heap_allocator(), init_filename); + String init_fullpath = make_string_c(fullpath_str); + TokenPos init_pos = {0}; + ImportedFile init_imported_file = {init_fullpath, init_fullpath, init_pos}; + array_add(&p->imports, init_imported_file); + p->init_fullpath = init_fullpath; + + { + String s = get_fullpath_core(heap_allocator(), str_lit("_preload.odin")); + ImportedFile runtime_file = {s, s, init_pos}; + array_add(&p->imports, runtime_file); + } + { + String s = get_fullpath_core(heap_allocator(), str_lit("_soft_numbers.odin")); + ImportedFile runtime_file = {s, s, init_pos}; + array_add(&p->imports, runtime_file); + } + + for_array(i, p->imports) { + ImportedFile imported_file = p->imports.e[i]; + String import_path = imported_file.path; + String import_rel_path = imported_file.rel_path; + TokenPos pos = imported_file.pos; + AstFile file = {0}; + ParseFileError err = init_ast_file(&file, import_path); + + if (err != ParseFile_None) { + if (pos.line != 0) { + gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); + } + gb_printf_err("Failed to parse file: %.*s\n\t", LIT(import_rel_path)); + switch (err) { + case ParseFile_WrongExtension: + gb_printf_err("Invalid file extension: File must have the extension `.odin`"); + break; + case ParseFile_InvalidFile: + gb_printf_err("Invalid file"); + break; + case ParseFile_EmptyFile: + gb_printf_err("File is empty"); + break; + case ParseFile_Permission: + gb_printf_err("File permissions problem"); + break; + case ParseFile_NotFound: + gb_printf_err("File cannot be found"); + break; + case ParseFile_InvalidToken: + gb_printf_err("Invalid token found in file"); + break; + } + gb_printf_err("\n"); + return err; + } + parse_file(p, &file); + + { + gb_mutex_lock(&p->mutex); + file.id = p->files.count; + array_add(&p->files, file); + gb_mutex_unlock(&p->mutex); + } + } + + for_array(i, p->files) { + p->total_token_count += p->files.e[i].tokens.count; + } + + + return ParseFile_None; +} + + diff --git a/src/printer.c b/src/printer.c new file mode 100644 index 000000000..4d7184631 --- /dev/null +++ b/src/printer.c @@ -0,0 +1,221 @@ + + +gb_inline void print_indent(isize indent) { + while (indent --> 0) + gb_printf(" "); +} + +void print_ast(AstNode *node, isize indent) { + if (node == NULL) + return; + + switch (node->kind) { + case AstNode_BasicLit: + print_indent(indent); + print_token(node->BasicLit); + break; + case AstNode_Ident: + print_indent(indent); + print_token(node->Ident); + break; + case AstNode_ProcLit: + print_indent(indent); + gb_printf("(proc lit)\n"); + print_ast(node->ProcLit.type, indent+1); + print_ast(node->ProcLit.body, indent+1); + break; + + case AstNode_CompoundLit: + print_indent(indent); + gb_printf("(compound lit)\n"); + print_ast(node->CompoundLit.type, indent+1); + for_array(i, node->CompoundLit.elems) { + print_ast(node->CompoundLit.elems[i], indent+1); + } + break; + + + case AstNode_TagExpr: + print_indent(indent); + gb_printf("(tag)\n"); + print_indent(indent+1); + print_token(node->TagExpr.name); + print_ast(node->TagExpr.expr, indent+1); + break; + + case AstNode_UnaryExpr: + print_indent(indent); + print_token(node->UnaryExpr.op); + print_ast(node->UnaryExpr.expr, indent+1); + break; + case AstNode_BinaryExpr: + print_indent(indent); + print_token(node->BinaryExpr.op); + print_ast(node->BinaryExpr.left, indent+1); + print_ast(node->BinaryExpr.right, indent+1); + break; + case AstNode_CallExpr: + print_indent(indent); + gb_printf("(call)\n"); + print_ast(node->CallExpr.proc, indent+1); + for_array(i, node->CallExpr.args) { + print_ast(node->CallExpr.args[i], indent+1); + } + break; + case AstNode_SelectorExpr: + print_indent(indent); + gb_printf(".\n"); + print_ast(node->SelectorExpr.expr, indent+1); + print_ast(node->SelectorExpr.selector, indent+1); + break; + case AstNode_IndexExpr: + print_indent(indent); + gb_printf("([])\n"); + print_ast(node->IndexExpr.expr, indent+1); + print_ast(node->IndexExpr.index, indent+1); + break; + case AstNode_DerefExpr: + print_indent(indent); + gb_printf("(deref)\n"); + print_ast(node->DerefExpr.expr, indent+1); + break; + + + case AstNode_ExprStmt: + print_ast(node->ExprStmt.expr, indent); + break; + case AstNode_IncDecStmt: + print_indent(indent); + print_token(node->IncDecStmt.op); + print_ast(node->IncDecStmt.expr, indent+1); + break; + case AstNode_AssignStmt: + print_indent(indent); + print_token(node->AssignStmt.op); + for_array(i, node->AssignStmt.lhs) { + print_ast(node->AssignStmt.lhs[i], indent+1); + } + for_array(i, node->AssignStmt.rhs) { + print_ast(node->AssignStmt.rhs[i], indent+1); + } + break; + case AstNode_BlockStmt: + print_indent(indent); + gb_printf("(block)\n"); + for_array(i, node->BlockStmt.stmts) { + print_ast(node->BlockStmt.stmts[i], indent+1); + } + break; + + case AstNode_IfStmt: + print_indent(indent); + gb_printf("(if)\n"); + print_ast(node->IfStmt.cond, indent+1); + print_ast(node->IfStmt.body, indent+1); + if (node->IfStmt.else_stmt) { + print_indent(indent); + gb_printf("(else)\n"); + print_ast(node->IfStmt.else_stmt, indent+1); + } + break; + case AstNode_ReturnStmt: + print_indent(indent); + gb_printf("(return)\n"); + for_array(i, node->ReturnStmt.results) { + print_ast(node->ReturnStmt.results[i], indent+1); + } + break; + case AstNode_ForStmt: + print_indent(indent); + gb_printf("(for)\n"); + print_ast(node->ForStmt.init, indent+1); + print_ast(node->ForStmt.cond, indent+1); + print_ast(node->ForStmt.post, indent+1); + print_ast(node->ForStmt.body, indent+1); + break; + case AstNode_DeferStmt: + print_indent(indent); + gb_printf("(defer)\n"); + print_ast(node->DeferStmt.stmt, indent+1); + break; + + + case AstNode_VarDecl: + print_indent(indent); + gb_printf("(decl:var)\n"); + for_array(i, node->VarDecl.names) { + print_ast(node->VarDecl.names[i], indent+1); + } + print_ast(node->VarDecl.type, indent+1); + for_array(i, node->VarDecl.values) { + print_ast(node->VarDecl.values[i], indent+1); + } + break; + case AstNode_ConstDecl: + print_indent(indent); + gb_printf("(decl:const)\n"); + for_array(i, node->VarDecl.names) { + print_ast(node->VarDecl.names[i], indent+1); + } + print_ast(node->VarDecl.type, indent+1); + for_array(i, node->VarDecl.values) { + print_ast(node->VarDecl.values[i], indent+1); + } + break; + case AstNode_ProcDecl: + print_indent(indent); + gb_printf("(decl:proc)\n"); + print_ast(node->ProcDecl.type, indent+1); + print_ast(node->ProcDecl.body, indent+1); + break; + + case AstNode_TypeDecl: + print_indent(indent); + gb_printf("(type)\n"); + print_ast(node->TypeDecl.name, indent+1); + print_ast(node->TypeDecl.type, indent+1); + break; + + case AstNode_ProcType: + print_indent(indent); + gb_printf("(type:proc)(%td -> %td)\n", node->ProcType.params.count, node->ProcType.results.count); + for_array(i, node->ProcType.params) { + print_ast(node->ProcType.params[i], indent+1); + } + if (node->ProcType.results.count > 0) { + print_indent(indent+1); + gb_printf("->\n"); + for_array(i, node->ProcType.results) { + print_ast(node->ProcType.results[i], indent+1); + } + } + break; + case AstNode_Parameter: + for_array(i, node->Parameter.names) { + print_ast(node->Parameter.names[i], indent+1); + } + print_ast(node->Parameter.type, indent); + break; + case AstNode_PointerType: + print_indent(indent); + print_token(node->PointerType.token); + print_ast(node->PointerType.type, indent+1); + break; + case AstNode_ArrayType: + print_indent(indent); + gb_printf("[]\n"); + print_ast(node->ArrayType.count, indent+1); + print_ast(node->ArrayType.elem, indent+1); + break; + case AstNode_StructType: + print_indent(indent); + gb_printf("(struct)\n"); + for_array(i, node->StructType.decls) { + print_ast(node->StructType.decls[i], indent+1); + } + break; + } + + // if (node->next) + // print_ast(node->next, indent); +} diff --git a/src/ssa.c b/src/ssa.c new file mode 100644 index 000000000..b34bc7c51 --- /dev/null +++ b/src/ssa.c @@ -0,0 +1,5419 @@ +typedef struct ssaProcedure ssaProcedure; +typedef struct ssaBlock ssaBlock; +typedef struct ssaValue ssaValue; +typedef struct ssaDebugInfo ssaDebugInfo; + +typedef Array(ssaValue *) ssaValueArray; + +#define MAP_TYPE ssaValue * +#define MAP_PROC map_ssa_value_ +#define MAP_NAME MapSsaValue +#include "map.c" + +#define MAP_TYPE ssaDebugInfo * +#define MAP_PROC map_ssa_debug_info_ +#define MAP_NAME MapSsaDebugInfo +#include "map.c" + +typedef struct ssaModule { + CheckerInfo * info; + BaseTypeSizes sizes; + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; + bool generate_debug_info; + + u32 stmt_state_flags; + + // String source_filename; + String layout; + // String triple; + + + MapEntity min_dep_map; // Key: Entity * + MapSsaValue values; // Key: Entity * + MapSsaValue members; // Key: String + MapString type_names; // Key: Type * + MapSsaDebugInfo debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice + + Array(ssaProcedure *) procs; // NOTE(bill): All procedures with bodies + ssaValueArray procs_to_generate; // NOTE(bill): Procedures to generate +} ssaModule; + +// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) +typedef struct ssaDomNode { + ssaBlock * idom; // Parent (Immediate Dominator) + Array(ssaBlock *) children; + i32 pre, post; // Ordering in tree +} ssaDomNode; + + +typedef struct ssaBlock { + i32 index; + String label; + ssaProcedure *parent; + AstNode * node; // Can be NULL + Scope * scope; + isize scope_index; + ssaDomNode dom; + i32 gaps; + + ssaValueArray instrs; + ssaValueArray locals; + + Array(ssaBlock *) preds; + Array(ssaBlock *) succs; +} ssaBlock; + +typedef struct ssaTargetList ssaTargetList; +struct ssaTargetList { + ssaTargetList *prev; + ssaBlock * break_; + ssaBlock * continue_; + ssaBlock * fallthrough_; +}; + +typedef enum ssaDeferExitKind { + ssaDeferExit_Default, + ssaDeferExit_Return, + ssaDeferExit_Branch, +} ssaDeferExitKind; +typedef enum ssaDeferKind { + ssaDefer_Node, + ssaDefer_Instr, +} ssaDeferKind; + +typedef struct ssaDefer { + ssaDeferKind kind; + isize scope_index; + ssaBlock * block; + union { + AstNode *stmt; + // NOTE(bill): `instr` will be copied every time to create a new one + ssaValue *instr; + }; +} ssaDefer; + +typedef struct ssaProcedure ssaProcedure; +struct ssaProcedure { + ssaProcedure * parent; + Array(ssaProcedure *) children; + + Entity * entity; + ssaModule * module; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; + + ssaValueArray params; + Array(ssaDefer) defer_stmts; + Array(ssaBlock *) blocks; + i32 scope_index; + ssaBlock * decl_block; + ssaBlock * entry_block; + ssaBlock * curr_block; + ssaTargetList * target_list; + ssaValueArray referrers; + + i32 local_count; + i32 instr_count; + i32 block_count; +}; + +#define SSA_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" +#define SSA_TYPE_INFO_DATA_NAME "__$type_info_data" +#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" + + +#define SSA_INSTR_KINDS \ + SSA_INSTR_KIND(Comment, struct { String text; }) \ + SSA_INSTR_KIND(Local, struct { \ + Entity * entity; \ + Type * type; \ + bool zero_initialized; \ + ssaValueArray referrers; \ + }) \ + SSA_INSTR_KIND(ZeroInit, struct { ssaValue *address; }) \ + SSA_INSTR_KIND(Store, struct { ssaValue *address, *value; }) \ + SSA_INSTR_KIND(Load, struct { Type *type; ssaValue *address; }) \ + SSA_INSTR_KIND(PtrOffset, struct { \ + ssaValue *address; \ + ssaValue *offset; \ + }) \ + SSA_INSTR_KIND(ArrayElementPtr, struct { \ + ssaValue *address; \ + Type * result_type; \ + ssaValue *elem_index; \ + }) \ + SSA_INSTR_KIND(StructElementPtr, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 elem_index; \ + }) \ + SSA_INSTR_KIND(ArrayExtractValue, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + SSA_INSTR_KIND(StructExtractValue, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + SSA_INSTR_KIND(UnionTagPtr, struct { \ + ssaValue *address; \ + Type *type; /* ^int */ \ + }) \ + SSA_INSTR_KIND(UnionTagValue, struct { \ + ssaValue *address; \ + Type *type; /* int */ \ + }) \ + SSA_INSTR_KIND(Conv, struct { \ + ssaConvKind kind; \ + ssaValue *value; \ + Type *from, *to; \ + }) \ + SSA_INSTR_KIND(Jump, struct { ssaBlock *block; }) \ + SSA_INSTR_KIND(If, struct { \ + ssaValue *cond; \ + ssaBlock *true_block; \ + ssaBlock *false_block; \ + }) \ + SSA_INSTR_KIND(Return, struct { ssaValue *value; }) \ + SSA_INSTR_KIND(Select, struct { \ + ssaValue *cond; \ + ssaValue *true_value; \ + ssaValue *false_value; \ + }) \ + SSA_INSTR_KIND(Phi, struct { ssaValueArray edges; Type *type; }) \ + SSA_INSTR_KIND(Unreachable, i32) \ + SSA_INSTR_KIND(BinaryOp, struct { \ + Type * type; \ + TokenKind op; \ + ssaValue *left, *right; \ + }) \ + SSA_INSTR_KIND(Call, struct { \ + Type * type; /* return type */ \ + ssaValue *value; \ + ssaValue **args; \ + isize arg_count; \ + }) \ + SSA_INSTR_KIND(VectorExtractElement, struct { \ + ssaValue *vector; \ + ssaValue *index; \ + }) \ + SSA_INSTR_KIND(VectorInsertElement, struct { \ + ssaValue *vector; \ + ssaValue *elem; \ + ssaValue *index; \ + }) \ + SSA_INSTR_KIND(VectorShuffle, struct { \ + ssaValue *vector; \ + i32 * indices; \ + i32 index_count; \ + Type * type; \ + }) \ + SSA_INSTR_KIND(StartupRuntime, i32) \ + SSA_INSTR_KIND(BoundsCheck, struct { \ + TokenPos pos; \ + ssaValue *index; \ + ssaValue *len; \ + }) \ + SSA_INSTR_KIND(SliceBoundsCheck, struct { \ + TokenPos pos; \ + ssaValue *low; \ + ssaValue *high; \ + ssaValue *max; \ + bool is_substring; \ + }) + +#define SSA_CONV_KINDS \ + SSA_CONV_KIND(trunc) \ + SSA_CONV_KIND(zext) \ + SSA_CONV_KIND(fptrunc) \ + SSA_CONV_KIND(fpext) \ + SSA_CONV_KIND(fptoui) \ + SSA_CONV_KIND(fptosi) \ + SSA_CONV_KIND(uitofp) \ + SSA_CONV_KIND(sitofp) \ + SSA_CONV_KIND(ptrtoint) \ + SSA_CONV_KIND(inttoptr) \ + SSA_CONV_KIND(bitcast) + +typedef enum ssaInstrKind { + ssaInstr_Invalid, +#define SSA_INSTR_KIND(x, ...) GB_JOIN2(ssaInstr_, x), + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +} ssaInstrKind; + +String const ssa_instr_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define SSA_INSTR_KIND(x, ...) {cast(u8 *)#x, gb_size_of(#x)-1}, + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +}; + +typedef enum ssaConvKind { + ssaConv_Invalid, +#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x), + SSA_CONV_KINDS +#undef SSA_CONV_KIND +} ssaConvKind; + +String const ssa_conv_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}, + SSA_CONV_KINDS +#undef SSA_CONV_KIND +}; + +#define SSA_INSTR_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(ssaInstr, k); + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND + +typedef struct ssaInstr ssaInstr; +struct ssaInstr { + ssaInstrKind kind; + + ssaBlock *parent; + Type *type; + + union { +#define SSA_INSTR_KIND(k, ...) GB_JOIN2(ssaInstr, k) k; + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND + }; +}; + + +typedef enum ssaValueKind { + ssaValue_Invalid, + + ssaValue_Constant, + ssaValue_ConstantSlice, + ssaValue_Nil, + ssaValue_TypeName, + ssaValue_Global, + ssaValue_Param, + + ssaValue_Proc, + ssaValue_Block, + ssaValue_Instr, + + ssaValue_Count, +} ssaValueKind; + +typedef struct ssaValueConstant { + Type * type; + ExactValue value; +} ssaValueConstant; + +typedef struct ssaValueConstantSlice { + Type * type; + ssaValue *backing_array; + i64 count; +} ssaValueConstantSlice; + +typedef struct ssaValueNil { + Type *type; +} ssaValueNil; + +typedef struct ssaValueTypeName { + Type * type; + String name; +} ssaValueTypeName; + +typedef struct ssaValueGlobal { + Entity * entity; + Type * type; + ssaValue * value; + ssaValueArray referrers; + bool is_constant; + bool is_private; + bool is_thread_local; + bool is_unnamed_addr; +} ssaValueGlobal; + +typedef struct ssaValueParam { + ssaProcedure *parent; + Entity * entity; + Type * type; + ssaValueArray referrers; +} ssaValueParam; + +typedef struct ssaValue { + ssaValueKind kind; + i32 index; + union { + ssaValueConstant Constant; + ssaValueConstantSlice ConstantSlice; + ssaValueNil Nil; + ssaValueTypeName TypeName; + ssaValueGlobal Global; + ssaValueParam Param; + ssaProcedure Proc; + ssaBlock Block; + ssaInstr Instr; + }; +} ssaValue; + +gb_global ssaValue *v_zero = NULL; +gb_global ssaValue *v_one = NULL; +gb_global ssaValue *v_zero32 = NULL; +gb_global ssaValue *v_one32 = NULL; +gb_global ssaValue *v_two32 = NULL; +gb_global ssaValue *v_false = NULL; +gb_global ssaValue *v_true = NULL; + +typedef enum ssaAddrKind { + ssaAddr_Default, + ssaAddr_Vector, +} ssaAddrKind; + +typedef struct ssaAddr { + ssaValue * addr; + AstNode * expr; // NOTE(bill): Just for testing - probably remove later + ssaAddrKind kind; + union { + struct { ssaValue *index; } Vector; + }; +} ssaAddr; + +ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) { + ssaAddr v = {addr, expr}; + return v; +} +ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) { + ssaAddr v = ssa_make_addr(addr, expr); + v.kind = ssaAddr_Vector; + v.Vector.index = index; + return v; +} + + + +typedef enum ssaDebugEncoding { + ssaDebugBasicEncoding_Invalid = 0, + + ssaDebugBasicEncoding_address = 1, + ssaDebugBasicEncoding_boolean = 2, + ssaDebugBasicEncoding_float = 3, + ssaDebugBasicEncoding_signed = 4, + ssaDebugBasicEncoding_signed_char = 5, + ssaDebugBasicEncoding_unsigned = 6, + ssaDebugBasicEncoding_unsigned_char = 7, + + ssaDebugBasicEncoding_member = 13, + ssaDebugBasicEncoding_pointer_type = 15, + ssaDebugBasicEncoding_typedef = 22, + + ssaDebugBasicEncoding_array_type = 1, + ssaDebugBasicEncoding_enumeration_type = 4, + ssaDebugBasicEncoding_structure_type = 19, + ssaDebugBasicEncoding_union_type = 23, + +} ssaDebugEncoding; + +typedef enum ssaDebugInfoKind { + ssaDebugInfo_Invalid, + + ssaDebugInfo_CompileUnit, + ssaDebugInfo_File, + ssaDebugInfo_Scope, + ssaDebugInfo_Proc, + ssaDebugInfo_AllProcs, + + ssaDebugInfo_BasicType, // basic types + ssaDebugInfo_ProcType, + ssaDebugInfo_DerivedType, // pointer, typedef + ssaDebugInfo_CompositeType, // array, struct, enum, (raw_)union + ssaDebugInfo_Enumerator, // For ssaDebugInfo_CompositeType if enum + ssaDebugInfo_GlobalVariable, + ssaDebugInfo_LocalVariable, + + + ssaDebugInfo_Count, +} ssaDebugInfoKind; + +typedef struct ssaDebugInfo ssaDebugInfo; +struct ssaDebugInfo { + ssaDebugInfoKind kind; + i32 id; + + union { + struct { + AstFile * file; + String producer; + ssaDebugInfo *all_procs; + } CompileUnit; + struct { + AstFile *file; + String filename; + String directory; + } File; + struct { + ssaDebugInfo *parent; + ssaDebugInfo *file; + TokenPos pos; + Scope * scope; // Actual scope + } Scope; + struct { + Entity * entity; + String name; + ssaDebugInfo *file; + TokenPos pos; + } Proc; + struct { + Array(ssaDebugInfo *) procs; + } AllProcs; + + + struct { + String name; + i32 size; + i32 align; + ssaDebugEncoding encoding; + } BasicType; + struct { + ssaDebugInfo * return_type; + Array(ssaDebugInfo *) param_types; + } ProcType; + struct { + ssaDebugInfo * base_type; + ssaDebugEncoding encoding; + } DerivedType; + struct { + ssaDebugEncoding encoding; + String name; + String identifier; + ssaDebugInfo * file; + TokenPos pos; + i32 size; + i32 align; + Array(ssaDebugInfo *) elements; + } CompositeType; + struct { + String name; + i64 value; + } Enumerator; + struct { + String name; + String linkage_name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + ssaValue *variable; + ssaDebugInfo *declaration; + } GlobalVariable; + struct { + String name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + i32 arg; // Non-zero if proc parameter + ssaDebugInfo *type; + } LocalVariable; + }; +}; + +typedef struct ssaGen { + ssaModule module; + gbFile output_file; + bool opt_called; +} ssaGen; + +ssaValue *ssa_lookup_member(ssaModule *m, String name) { + ssaValue **v = map_ssa_value_get(&m->members, hash_string(name)); + if (v != NULL) { + return *v; + } + return NULL; +} + + +Type *ssa_type(ssaValue *value); +Type *ssa_instr_type(ssaInstr *instr) { + switch (instr->kind) { + case ssaInstr_Local: + return instr->Local.type; + case ssaInstr_Load: + return instr->Load.type; + case ssaInstr_StructElementPtr: + return instr->StructElementPtr.result_type; + case ssaInstr_ArrayElementPtr: + return instr->ArrayElementPtr.result_type; + case ssaInstr_PtrOffset: + return ssa_type(instr->PtrOffset.address); + case ssaInstr_Phi: + return instr->Phi.type; + case ssaInstr_ArrayExtractValue: + return instr->ArrayExtractValue.result_type; + case ssaInstr_StructExtractValue: + return instr->StructExtractValue.result_type; + case ssaInstr_UnionTagPtr: + return instr->UnionTagPtr.type; + case ssaInstr_UnionTagValue: + return instr->UnionTagValue.type; + case ssaInstr_BinaryOp: + return instr->BinaryOp.type; + case ssaInstr_Conv: + return instr->Conv.to; + case ssaInstr_Select: + return ssa_type(instr->Select.true_value); + case ssaInstr_Call: { + Type *pt = base_type(instr->Call.type); + if (pt != NULL) { + if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) { + return pt->Tuple.variables[0]->type; + } + return pt; + } + return NULL; + } break; + case ssaInstr_VectorExtractElement: { + Type *vt = ssa_type(instr->VectorExtractElement.vector); + Type *bt = base_vector_type(vt); + GB_ASSERT(!is_type_vector(bt)); + return bt; + } break; + case ssaInstr_VectorInsertElement: + return ssa_type(instr->VectorInsertElement.vector); + case ssaInstr_VectorShuffle: + return instr->VectorShuffle.type; + } + return NULL; +} + +Type *ssa_type(ssaValue *value) { + switch (value->kind) { + case ssaValue_Constant: + return value->Constant.type; + case ssaValue_ConstantSlice: + return value->ConstantSlice.type; + case ssaValue_Nil: + return value->Nil.type; + case ssaValue_TypeName: + return value->TypeName.type; + case ssaValue_Global: + return value->Global.type; + case ssaValue_Param: + return value->Param.type; + case ssaValue_Proc: + return value->Proc.type; + case ssaValue_Instr: + return ssa_instr_type(&value->Instr); + } + return NULL; +} + +Type *ssa_addr_type(ssaAddr lval) { + if (lval.addr != NULL) { + Type *t = ssa_type(lval.addr); + GB_ASSERT(is_type_pointer(t)); + return type_deref(t); + } + return NULL; +} + + + +bool ssa_is_blank_ident(AstNode *node) { + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + return is_blank_ident(i->string); + } + return false; +} + + +ssaInstr *ssa_get_last_instr(ssaBlock *block) { + if (block != NULL) { + isize len = block->instrs.count; + if (len > 0) { + ssaValue *v = block->instrs.e[len-1]; + GB_ASSERT(v->kind == ssaValue_Instr); + return &v->Instr; + } + } + return NULL; + +} + +bool ssa_is_instr_terminating(ssaInstr *i) { + if (i != NULL) { + switch (i->kind) { + case ssaInstr_Return: + case ssaInstr_Unreachable: + return true; + } + } + + return false; +} + + +void ssa_add_edge(ssaBlock *from, ssaBlock *to) { + array_add(&from->succs, to); + array_add(&to->preds, from); +} + +void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) { + if (instr->kind == ssaValue_Instr) { + instr->Instr.parent = parent; + } +} + +ssaValueArray *ssa_value_referrers(ssaValue *v) { + switch (v->kind) { + case ssaValue_Global: + return &v->Global.referrers; + case ssaValue_Param: + return &v->Param.referrers; + case ssaValue_Proc: { + if (v->Proc.parent != NULL) { + return &v->Proc.referrers; + } + return NULL; + } + case ssaValue_Instr: { + ssaInstr *i = &v->Instr; + switch (i->kind) { + case ssaInstr_Local: + return &i->Local.referrers; + } + } break; + } + + return NULL; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Make +// +//////////////////////////////////////////////////////////////// + +void ssa_module_add_value (ssaModule *m, Entity *e, ssaValue *v); +ssaValue *ssa_emit_zero_init (ssaProcedure *p, ssaValue *address); +ssaValue *ssa_emit_comment (ssaProcedure *p, String text); +ssaValue *ssa_emit_store (ssaProcedure *p, ssaValue *address, ssaValue *value); +ssaValue *ssa_emit_load (ssaProcedure *p, ssaValue *address); +void ssa_emit_jump (ssaProcedure *proc, ssaBlock *block); +ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t); +ssaValue *ssa_type_info (ssaProcedure *proc, Type *type); +ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr); +void ssa_build_stmt (ssaProcedure *proc, AstNode *node); +void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); +void ssa_build_defer_stmt (ssaProcedure *proc, ssaDefer d); +ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr); +void ssa_build_proc (ssaValue *value, ssaProcedure *parent); +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name); + + + + +ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) { + ssaValue *v = gb_alloc_item(a, ssaValue); + v->kind = kind; + return v; +} +ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) { + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr); + v->Instr.kind = kind; + proc->instr_count++; + return v; +} +ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) { + ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo); + di->kind = kind; + return di; +} + + + + +ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName); + v->TypeName.name = name; + v->TypeName.type = type; + return v; +} + +ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Global); + v->Global.entity = e; + v->Global.type = make_type_pointer(a, e->type); + v->Global.value = value; + array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Param); + v->Param.parent = parent; + v->Param.entity = e; + v->Param.type = e->type; + array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Nil); + v->Nil.type = type; + return v; +} + + + +ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, bool 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; + array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + ssa_module_add_value(p->module, e, v); + return v; +} + + +ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store); + ssaInstr *i = &v->Instr; + i->Store.address = address; + i->Store.value = value; + return v; +} + +ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit); + ssaInstr *i = &v->Instr; + i->ZeroInit.address = address; + return v; +} + +ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load); + ssaInstr *i = &v->Instr; + i->Load.address = address; + i->Load.type = type_deref(ssa_type(address)); + return v; +} + +ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr); + ssaInstr *i = &v->Instr; + Type *t = ssa_type(address); + GB_ASSERT(is_type_pointer(t)); + t = base_type(type_deref(t)); + GB_ASSERT(is_type_array(t) || is_type_vector(t)); + + Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem); + + i->ArrayElementPtr.address = address; + i->ArrayElementPtr.elem_index = elem_index; + i->ArrayElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr); + ssaInstr *i = &v->Instr; + i->StructElementPtr.address = address; + i->StructElementPtr.elem_index = elem_index; + i->StructElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset); + ssaInstr *i = &v->Instr; + i->PtrOffset.address = address; + i->PtrOffset.offset = offset; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + GB_ASSERT_MSG(is_type_integer(ssa_type(offset)), + "%s", type_to_string(ssa_type(address))); + + return v; +} + + + +ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue); + ssaInstr *i = &v->Instr; + i->ArrayExtractValue.address = address; + i->ArrayExtractValue.index = index; + Type *t = base_type(ssa_type(address)); + GB_ASSERT(is_type_array(t)); + i->ArrayExtractValue.result_type = t->Array.elem; + return v; +} + +ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue); + ssaInstr *i = &v->Instr; + i->StructExtractValue.address = address; + i->StructExtractValue.index = index; + i->StructExtractValue.result_type = result_type; + return v; +} + +ssaValue *ssa_make_instr_union_tag_ptr(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagPtr); + ssaInstr *i = &v->Instr; + i->UnionTagPtr.address = address; + i->UnionTagPtr.type = t_int_ptr; + return v; +} + +ssaValue *ssa_make_instr_union_tag_value(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagValue); + ssaInstr *i = &v->Instr; + i->UnionTagValue.address = address; + i->UnionTagValue.type = t_int_ptr; + return v; +} + + +ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp); + ssaInstr *i = &v->Instr; + i->BinaryOp.op = op; + i->BinaryOp.left = left; + i->BinaryOp.right = right; + i->BinaryOp.type = type; + return v; +} + +ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump); + ssaInstr *i = &v->Instr; + i->Jump.block = block; + return v; +} +ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_If); + ssaInstr *i = &v->Instr; + i->If.cond = cond; + i->If.true_block = true_block; + i->If.false_block = false_block; + return v; +} + + +ssaValue *ssa_make_instr_phi(ssaProcedure *p, ssaValueArray edges, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi); + ssaInstr *i = &v->Instr; + i->Phi.edges = edges; + i->Phi.type = type; + return v; +} + +ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable); + return v; +} + +ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return); + v->Instr.Return.value = value; + return v; +} + +ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select); + v->Instr.Select.cond = cond; + v->Instr.Select.true_value = t; + v->Instr.Select.false_value = f; + return v; +} + +ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call); + v->Instr.Call.value = value; + v->Instr.Call.args = args; + v->Instr.Call.arg_count = arg_count; + v->Instr.Call.type = result_type; + return v; +} + +ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv); + v->Instr.Conv.kind = kind; + v->Instr.Conv.value = value; + v->Instr.Conv.from = from; + v->Instr.Conv.to = to; + return v; +} + +ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement); + v->Instr.VectorExtractElement.vector = vector; + v->Instr.VectorExtractElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement); + v->Instr.VectorInsertElement.vector = vector; + v->Instr.VectorInsertElement.elem = elem; + v->Instr.VectorInsertElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle); + v->Instr.VectorShuffle.vector = vector; + v->Instr.VectorShuffle.indices = indices; + v->Instr.VectorShuffle.index_count = index_count; + + Type *vt = base_type(ssa_type(vector)); + v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); + + return v; +} + +ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment); + v->Instr.Comment.text = text; + return v; +} + +ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck); + v->Instr.BoundsCheck.pos = pos; + v->Instr.BoundsCheck.index = index; + v->Instr.BoundsCheck.len = len; + return v; +} +ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck); + v->Instr.SliceBoundsCheck.pos = pos; + v->Instr.SliceBoundsCheck.low = low; + v->Instr.SliceBoundsCheck.high = high; + v->Instr.SliceBoundsCheck.max = max; + v->Instr.SliceBoundsCheck.is_substring = is_substring; + return v; +} + + + +ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Constant); + v->Constant.type = type; + v->Constant.value = value; + return v; +} + + +ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) { + ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice); + v->ConstantSlice.type = type; + v->ConstantSlice.backing_array = backing_array; + v->ConstantSlice.count = count; + return v; +} + +ssaValue *ssa_make_const_int(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_int, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_bool(gbAllocator a, bool b) { + return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); +} +ssaValue *ssa_make_const_string(gbAllocator a, String s) { + return ssa_make_value_constant(a, t_string, make_exact_value_string(s)); +} + +ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Proc); + v->Proc.module = m; + v->Proc.entity = entity; + v->Proc.type = type; + v->Proc.type_expr = type_expr; + v->Proc.body = body; + v->Proc.name = name; + array_init(&v->Proc.referrers, heap_allocator()); // TODO(bill): replace heap allocator + + Type *t = base_type(type); + GB_ASSERT(is_type_proc(t)); + array_init_reserve(&v->Proc.params, heap_allocator(), t->Proc.param_count); + + return v; +} + +ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) { + Scope *scope = NULL; + if (node != NULL) { + Scope **found = map_scope_get(&proc->module->info->scopes, hash_pointer(node)); + if (found) { + scope = *found; + } else { + GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind])); + } + } + + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block); + v->Block.label = make_string_c(label); + v->Block.node = node; + v->Block.scope = scope; + v->Block.parent = proc; + + array_init(&v->Block.instrs, heap_allocator()); + array_init(&v->Block.locals, heap_allocator()); + + array_init(&v->Block.preds, heap_allocator()); + array_init(&v->Block.succs, heap_allocator()); + + ssaBlock *block = &v->Block; + + array_add(&proc->blocks, block); + proc->block_count++; + + return block; +} + + + + + +ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) { + ssaDefer d = {ssaDefer_Node}; + d.scope_index = scope_index; + d.block = proc->curr_block; + d.stmt = stmt; + array_add(&proc->defer_stmts, d); + return d; +} + + +ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) { + ssaDefer d = {ssaDefer_Instr}; + d.scope_index = proc->scope_index; + d.block = proc->curr_block; + d.instr = instr; // NOTE(bill): It will make a copy everytime it is called + array_add(&proc->defer_stmts, d); + return d; +} + + + +ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { + gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); + + if (is_type_slice(type)) { + ast_node(cl, CompoundLit, value.value_compound); + + isize count = cl->elems.count; + if (count == 0) { + return ssa_make_value_nil(a, type); + } + Type *elem = base_type(type)->Slice.elem; + Type *t = make_type_array(a, elem, count); + ssaValue *backing_array = ssa_add_module_constant(m, t, value); + + + isize max_len = 7+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index); + m->global_array_index++; + + String name = make_string(str, len-1); + + Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value); + ssaValue *g = ssa_make_value_global(a, e, backing_array); + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + + return ssa_make_value_constant_slice(a, type, g, count); + } + + return ssa_make_value_constant(a, type, value); +} + +ssaValue *ssa_add_global_string_array(ssaModule *m, String string) { + // TODO(bill): Should this use the arena allocator or the heap allocator? + // Strings could be huge! + gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); + + isize max_len = 6+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index); + m->global_string_index++; + + String name = make_string(str, len-1); + Token token = {Token_String}; + token.string = name; + Type *type = make_type_array(a, t_u8, string.len); + ExactValue ev = make_exact_value_string(string); + Entity *entity = make_entity_constant(a, NULL, token, type, ev); + ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev)); + g->Global.is_private = true; + // g->Global.is_unnamed_addr = true; + // g->Global.is_constant = true; + + ssa_module_add_value(m, entity, g); + map_ssa_value_set(&m->members, hash_string(name), g); + + return g; +} + + + + +ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e) { + ssaBlock *b = proc->decl_block; // all variables must be in the first block + ssaValue *instr = ssa_make_instr_local(proc, e, true); + instr->Instr.parent = b; + array_add(&b->instrs, instr); + array_add(&b->locals, instr); + proc->local_count++; + + // if (zero_initialized) { + ssa_emit_zero_init(proc, instr); + // } + + return instr; +} + +ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, bool zero_initialized) { + Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(name)); + if (found) { + Entity *e = *found; + ssa_emit_comment(proc, e->token.string); + return ssa_add_local(proc, e); + } + return NULL; +} + +ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { + GB_ASSERT(type != NULL); + + Scope *scope = NULL; + if (proc->curr_block) { + scope = proc->curr_block->scope; + } + Entity *e = make_entity_variable(proc->module->allocator, + scope, + empty_token, + type); + return ssa_add_local(proc, e); +} + +ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) { + ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e); +#if 1 + ssaValue *l = ssa_add_local(proc, e); + ssa_emit_store(proc, l, v); +#else + ssa_module_add_value(proc->module, e, v); +#endif + return v; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Debug +// +//////////////////////////////////////////////////////////////// + +ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(file != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File); + di->File.file = file; + + String filename = file->tokenizer.fullpath; + String directory = filename; + isize slash_index = 0; + for (isize i = filename.len-1; i >= 0; i--) { + if (filename.text[i] == '\\' || + filename.text[i] == '/') { + break; + } + slash_index = i; + } + directory.len = slash_index-1; + filename.text = filename.text + slash_index; + filename.len -= slash_index; + + + di->File.filename = filename; + di->File.directory = directory; + + map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(file), di); + return di; +} + + +ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(entity != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc); + di->Proc.entity = entity; + di->Proc.name = name; + di->Proc.file = file; + di->Proc.pos = entity->token.pos; + + map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(entity), di); + return di; +} + +//////////////////////////////////////////////////////////////// +// +// @Emit +// +//////////////////////////////////////////////////////////////// + + +ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { + GB_ASSERT(instr->kind == ssaValue_Instr); + ssaBlock *b = proc->curr_block; + instr->Instr.parent = b; + if (b != NULL) { + ssaInstr *i = ssa_get_last_instr(b); + if (!ssa_is_instr_terminating(i)) { + array_add(&b->instrs, instr); + } + } + return instr; +} +ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + return ssa_emit(p, ssa_make_instr_store(p, address, value)); +} +ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_load(p, address)); +} +ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + return ssa_emit(p, ssa_make_instr_select(p, cond, t, f)); +} + +ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_zero_init(p, address)); +} + +ssaValue *ssa_emit_comment(ssaProcedure *p, String text) { + return ssa_emit(p, ssa_make_instr_comment(p, text)); +} + + +ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) { + Type *pt = base_type(ssa_type(value)); + GB_ASSERT(pt->kind == Type_Proc); + Type *results = pt->Proc.results; + return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results)); +} + +ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) { + String name = make_string_c(name_); + ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); + ssaValue *gp = *found; + return ssa_emit_call(proc, gp, args, arg_count); +} + + + +void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + isize count = proc->defer_stmts.count; + isize i = count; + while (i --> 0) { + ssaDefer d = proc->defer_stmts.e[i]; + if (kind == ssaDeferExit_Default) { + if (proc->scope_index == d.scope_index && + d.scope_index > 1) { + ssa_build_defer_stmt(proc, d); + array_pop(&proc->defer_stmts); + continue; + } else { + break; + } + } else if (kind == ssaDeferExit_Return) { + ssa_build_defer_stmt(proc, d); + } else if (kind == ssaDeferExit_Branch) { + GB_ASSERT(block != NULL); + isize lower_limit = block->scope_index+1; + if (lower_limit < d.scope_index) { + ssa_build_defer_stmt(proc, d); + } + } + } +} + + +void ssa_open_scope(ssaProcedure *proc) { + proc->scope_index++; +} + +void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + ssa_emit_defer_stmts(proc, kind, block); + GB_ASSERT(proc->scope_index > 0); + proc->scope_index--; +} + + + +void ssa_emit_unreachable(ssaProcedure *proc) { + ssa_emit(proc, ssa_make_instr_unreachable(proc)); +} + +void ssa_emit_return(ssaProcedure *proc, ssaValue *v) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL); + ssa_emit(proc, ssa_make_instr_return(proc, v)); +} + +void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_jump(proc, target_block)); + ssa_add_edge(b, target_block); + proc->curr_block = NULL; +} + +void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block)); + ssa_add_edge(b, true_block); + ssa_add_edge(b, false_block); + proc->curr_block = NULL; +} + +void ssa_emit_startup_runtime(ssaProcedure *proc) { + GB_ASSERT(proc->parent == NULL && str_eq(proc->name, str_lit("main"))); + ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime)); +} + + + + +ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + Type *elem_type = base_type(ssa_type(v))->Vector.elem; + ssaValue *elem = ssa_emit_conv(proc, value, elem_type); + ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index)); + return ssa_emit_store(proc, addr.addr, out); + } else { + ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr)); + return ssa_emit_store(proc, addr.addr, v); + } +} +ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) { + if (addr.addr == NULL) { + GB_PANIC("Illegal addr load"); + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index)); + } + Type *t = base_type(ssa_type(addr.addr)); + if (t->kind == Type_Proc) { + // NOTE(bill): Imported procedures don't require a load as they are pointers + return addr.addr; + } + return ssa_emit_load(proc, addr.addr); +} + + + + +ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) { + offset = ssa_emit_conv(proc, offset, t_int); + return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset)); +} + +ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + Type *t_left = ssa_type(left); + Type *t_right = ssa_type(right); + + if (op == Token_Add) { + if (is_type_pointer(t_left)) { + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(ssa_type(right))) { + ssaValue *ptr = ssa_emit_conv(proc, right, type); + ssaValue *offset = left; + return ssa_emit_ptr_offset(proc, ptr, offset); + } + } else if (op == Token_Sub) { + if (is_type_pointer(t_left) && is_type_integer(t_right)) { + // ptr - int + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(t_left) && is_type_pointer(t_right)) { + GB_ASSERT(is_type_integer(type)); + Type *ptr_type = t_left; + ssaModule *m = proc->module; + ssaValue *x = ssa_emit_conv(proc, left, type); + ssaValue *y = ssa_emit_conv(proc, right, type); + ssaValue *diff = ssa_emit_arith(proc, op, x, y, type); + ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); + return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type); + } + } + + + switch (op) { + case Token_AndNot: { + // NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1) + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1)); + op = Token_Xor; + right = ssa_emit_arith(proc, op, right, neg, type); + GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp); + right->Instr.BinaryOp.type = type; + op = Token_And; + } /* fallthrough */ + case Token_Add: + case Token_Sub: + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_Or: + case Token_Xor: + case Token_Shl: + case Token_Shr: + left = ssa_emit_conv(proc, left, type); + right = ssa_emit_conv(proc, right, type); + break; + } + + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type)); +} + +ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) { + Type *a = base_type(ssa_type(left)); + Type *b = base_type(ssa_type(right)); + + if (are_types_identical(a, b)) { + // NOTE(bill): No need for a conversion + } else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) { + left = ssa_emit_conv(proc, left, ssa_type(right)); + } else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) { + right = ssa_emit_conv(proc, right, ssa_type(left)); + } + + Type *result = t_bool; + if (is_type_vector(a)) { + result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); + } + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result)); +} + +ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) { + GB_ASSERT(index != NULL); + Type *st = base_type(type_deref(ssa_type(s))); + GB_ASSERT(is_type_array(st) || is_type_vector(st)); + + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + index = ssa_emit_conv(proc, index, t_i32); + return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index)); +} + +ssaValue *ssa_emit_array_epi(ssaProcedure *proc, ssaValue *s, i32 index) { + return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index)); +} + +ssaValue *ssa_emit_union_tag_ptr(ssaProcedure *proc, ssaValue *u) { + Type *t = ssa_type(u); + GB_ASSERT(is_type_pointer(t) && + is_type_union(type_deref(t))); + return ssa_emit(proc, ssa_make_instr_union_tag_ptr(proc, u)); +} + +ssaValue *ssa_emit_union_tag_value(ssaProcedure *proc, ssaValue *u) { + Type *t = ssa_type(u); + GB_ASSERT(is_type_union(t)); + return ssa_emit(proc, ssa_make_instr_union_tag_value(proc, u)); +} + + + +ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) { + gbAllocator a = proc->module->allocator; + Type *t = base_type(type_deref(ssa_type(s))); + Type *result_type = NULL; + ssaValue *gep = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = make_type_pointer(a, t->Tuple.variables[index]->type); + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; + case 1: result_type = make_type_pointer(a, t_int); break; + case 2: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_u8_ptr); break; + case 1: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_type_info_ptr); break; + case 1: result_type = make_type_pointer(a, t_rawptr); break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Maybe.elem); break; + case 1: result_type = make_type_pointer(a, t_bool); break; + } + } else { + GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type); + return ssa_emit(proc, gep); +} + + + +ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + Type *st = base_type(ssa_type(s)); + GB_ASSERT(is_type_array(st)); + return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index)); +} + +ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + + gbAllocator a = proc->module->allocator; + Type *t = base_type(ssa_type(s)); + Type *result_type = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = t->Record.fields[index]->type; + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = t->Tuple.variables[index]->type; + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Slice.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = t_u8_ptr; break; + case 1: result_type = t_int; break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = t_type_info_ptr; break; + case 1: result_type = t_rawptr; break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = t->Maybe.elem; break; + case 1: result_type = t_bool; break; + } + } else { + GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type)); +} + + +ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + } else if (type->kind == Type_Record) { + type = type->Record.fields[index]->type; + e = ssa_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_struct_ep(proc, e, index); + } break; + + case Basic_string: + e = ssa_emit_struct_ep(proc, e, index); + break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else if (type->kind == Type_Slice) { + e = ssa_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Vector) { + e = ssa_emit_array_epi(proc, e, index); + } else if (type->kind == Type_Array) { + e = ssa_emit_array_epi(proc, e, index); + } else { + GB_PANIC("un-gep-able type"); + } + } + + return e; +} + + +ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?"); + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, type); + } else { + e = ssa_emit_struct_ev(proc, e, index); + } + } + + return e; +} + + + + +ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { + return ssa_emit_array_ep(proc, array, v_zero32); +} +ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) { + Type *t = ssa_type(array); + GB_ASSERT(t->kind == Type_Array); + return ssa_make_const_int(proc->module->allocator, t->Array.count); +} +ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) { + return ssa_array_len(proc, array); +} + +ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 0); +} +ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 1); +} +ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 2); +} + +ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 0); +} +ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 1); +} + + + +ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) { + // TODO(bill): array bounds checking for slice creation + // TODO(bill): check that low < high <= max + gbAllocator a = proc->module->allocator; + Type *bt = base_type(ssa_type(base)); + + if (low == NULL) { + low = v_zero; + } + if (high == NULL) { + switch (bt->kind) { + case Type_Array: high = ssa_array_len(proc, base); break; + case Type_Slice: high = ssa_slice_len(proc, base); break; + case Type_Pointer: high = v_one; break; + } + } + if (max == NULL) { + switch (bt->kind) { + case Type_Array: max = ssa_array_cap(proc, base); break; + case Type_Slice: max = ssa_slice_cap(proc, base); break; + case Type_Pointer: max = high; break; + } + } + GB_ASSERT(max != NULL); + + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + + ssaValue *elem = NULL; + switch (bt->kind) { + case Type_Array: elem = ssa_array_elem(proc, base); break; + case Type_Slice: elem = ssa_slice_elem(proc, base); break; + case Type_Pointer: elem = ssa_emit_load(proc, base); break; + } + + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep = NULL; + gep = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, gep, elem); + + gep = ssa_emit_struct_ep(proc, slice, 1); + ssa_emit_store(proc, gep, len); + + gep = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep, cap); + + return slice; +} + +ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0); + ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, str_elem, elem); + ssa_emit_store(proc, str_len, len); + return ssa_emit_load(proc, str); +} + + + + +String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { + Type *prev_src = src; + // Type *prev_dst = dst; + src = base_type(type_deref(src)); + // dst = base_type(type_deref(dst)); + bool src_is_ptr = src != prev_src; + // bool dst_is_ptr = dst != prev_dst; + + GB_ASSERT(is_type_struct(src)); + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) { + if (are_types_identical(dst, f->type)) { + return f->token.string; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(type_deref(dst), f->type)) { + return f->token.string; + } + } + if (is_type_struct(f->type)) { + String name = lookup_polymorphic_field(info, dst, f->type); + if (name.len > 0) { + return name; + } + } + } + } + return str_lit(""); +} + +ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type)); +} + + +ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(get_enum_base_type(src_type)); + Type *dst = base_type(get_enum_base_type(t)); + + if (value->kind == ssaValue_Constant) { + if (is_type_any(dst)) { + ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type)); + ssa_emit_store(proc, default_value, value); + return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any); + } else if (dst->kind == Type_Basic) { + ExactValue ev = value->Constant.value; + if (is_type_float(dst)) { + ev = exact_value_to_float(ev); + } else if (is_type_string(dst)) { + // Handled elsewhere + GB_ASSERT(ev.kind == ExactValue_String); + } else if (is_type_integer(dst)) { + ev = exact_value_to_integer(ev); + } else if (is_type_pointer(dst)) { + // IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null` + ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev); + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst)); + } + return ssa_add_module_constant(proc->module, t, ev); + } + } + + if (are_types_identical(src, dst)) { + return value; + } + + if (is_type_maybe(dst)) { + ssaValue *maybe = ssa_add_local_generated(proc, dst); + ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0); + ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1); + ssa_emit_store(proc, val, value); + ssa_emit_store(proc, set, v_true); + return ssa_emit_load(proc, maybe); + } + + // integer -> integer + if (is_type_integer(src) && is_type_integer(dst)) { + GB_ASSERT(src->kind == Type_Basic && + dst->kind == Type_Basic); + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + if (sz == dz) { + // NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment + return value; + } + + ssaConvKind kind = ssaConv_trunc; + if (dz >= sz) { + kind = ssaConv_zext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // boolean -> integer + if (is_type_boolean(src) && is_type_integer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst)); + } + + // integer -> boolean + if (is_type_integer(src) && is_type_boolean(dst)) { + return ssa_emit_comp(proc, Token_NotEq, value, v_zero); + } + + + // float -> float + if (is_type_float(src) && is_type_float(dst)) { + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + ssaConvKind kind = ssaConv_fptrunc; + if (dz >= sz) { + kind = ssaConv_fpext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // float <-> integer + if (is_type_float(src) && is_type_integer(dst)) { + ssaConvKind kind = ssaConv_fptosi; + if (is_type_unsigned(dst)) { + kind = ssaConv_fptoui; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + if (is_type_integer(src) && is_type_float(dst)) { + ssaConvKind kind = ssaConv_sitofp; + if (is_type_unsigned(src)) { + kind = ssaConv_uitofp; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // Pointer <-> int + if (is_type_pointer(src) && is_type_int_or_uint(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst)); + } + if (is_type_int_or_uint(src) && is_type_pointer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); + } + + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, src_type)) { + ssa_emit_comment(proc, str_lit("union - child to parent")); + gbAllocator allocator = proc->module->allocator; + ssaValue *parent = ssa_add_local_generated(proc, t); + ssaValue *tag = ssa_make_const_int(allocator, i); + ssa_emit_store(proc, ssa_emit_union_tag_ptr(proc, parent), tag); + + ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); + + Type *tag_type = src_type; + Type *tag_type_ptr = make_type_pointer(allocator, tag_type); + ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr); + ssa_emit_store(proc, underlying, value); + + return ssa_emit_load(proc, parent); + } + } + } + + // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's + // subtype polymorphism casting + { + Type *sb = base_type(type_deref(src)); + bool src_is_ptr = src != sb; + if (is_type_struct(sb)) { + String field_name = lookup_polymorphic_field(proc->module->info, t, src); + // gb_printf("field_name: %.*s\n", LIT(field_name)); + if (field_name.len > 0) { + // NOTE(bill): It can be casted + Selection sel = lookup_field(proc->module->allocator, sb, field_name, false); + if (sel.entity != NULL) { + ssa_emit_comment(proc, str_lit("cast - polymorphism")); + if (src_is_ptr) { + value = ssa_emit_load(proc, value); + } + return ssa_emit_deep_field_ev(proc, sb, value, sel); + } + } + } + } + + + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // proc <-> proc + if (is_type_proc(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + // pointer -> proc + if (is_type_pointer(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + // proc -> pointer + if (is_type_proc(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // []byte/[]u8 <-> string + if (is_type_u8_slice(src) && is_type_string(dst)) { + ssaValue *elem = ssa_slice_elem(proc, value); + ssaValue *len = ssa_slice_len(proc, value); + return ssa_emit_string(proc, elem, len); + } + if (is_type_string(src) && is_type_u8_slice(dst)) { + ssaValue *elem = ssa_string_elem(proc, value); + ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem)); + ssa_emit_store(proc, elem_ptr, elem); + + ssaValue *len = ssa_string_len(proc, value); + ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len); + return ssa_emit_load(proc, slice); + } + + if (is_type_vector(dst)) { + Type *dst_elem = dst->Vector.elem; + value = ssa_emit_conv(proc, value, dst_elem); + ssaValue *v = ssa_add_local_generated(proc, t); + v = ssa_emit_load(proc, v); + v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); + // NOTE(bill): Broadcast lowest value to all values + isize index_count = dst->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + + v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count)); + return v; + } + + if (is_type_any(dst)) { + ssaValue *result = ssa_add_local_generated(proc, t_any); + + if (is_type_untyped_nil(src)) { + return ssa_emit_load(proc, result); + } + + ssaValue *data = NULL; + if (value->kind == ssaValue_Instr && + value->Instr.kind == ssaInstr_Load) { + // NOTE(bill): Addressable value + data = value->Instr.Load.address; + } else { + // NOTE(bill): Non-addressable value + data = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, data, value); + } + GB_ASSERT(is_type_pointer(ssa_type(data))); + GB_ASSERT(is_type_typed(src_type)); + data = ssa_emit_conv(proc, data, t_rawptr); + + + ssaValue *ti = ssa_type_info(proc, src_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1); + ssa_emit_store(proc, gep0, ti); + ssa_emit_store(proc, gep1, data); + + return ssa_emit_load(proc, result); + } + + if (is_type_untyped_nil(src) && type_has_nil(dst)) { + return ssa_make_value_nil(proc->module->allocator, t); + } + + + gb_printf_err("ssa_emit_conv: src -> dst\n"); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); + + + GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + +bool ssa_is_type_aggregate(Type *t) { + t = base_type(get_enum_base_type(t)); + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_string: + case Basic_any: + return true; + } + break; + + case Type_Pointer: + case Type_Vector: + return false; + + case Type_Array: + case Type_Slice: + case Type_Maybe: + case Type_Record: + case Type_Tuple: + return true; + + case Type_Named: + return ssa_is_type_aggregate(t->Named.base); + } + + return false; +} + +ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(src_type); + Type *dst = base_type(t); + if (are_types_identical(t, src_type)) { + return value; + } + + ssaModule *m = proc->module; + + i64 sz = type_size_of(m->sizes, m->allocator, src); + i64 dz = type_size_of(m->sizes, m->allocator, dst); + + GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + if (ssa_is_type_aggregate(src) || ssa_is_type_aggregate(dst)) { + ssaValue *s = ssa_add_local_generated(proc, src); + ssa_emit_store(proc, s, value); + + ssaValue *d = ssa_emit_bitcast(proc, s, make_type_pointer(m->allocator, dst)); + return ssa_emit_load(proc, d); + } + + // TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM + + return ssa_emit_bitcast(proc, value, dst); +} + +ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) { + GB_ASSERT(is_type_pointer(ssa_type(value))); + gbAllocator allocator = proc->module->allocator; + + String field_name = check_down_cast_name(t, type_deref(ssa_type(value))); + GB_ASSERT(field_name.len > 0); + Selection sel = lookup_field(proc->module->allocator, t, field_name, false); + ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr); + + i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); + ssaValue *offset = ssa_make_const_int(allocator, -offset_); + ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset); + return ssa_emit_conv(proc, head, t); +} + +ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) { + GB_ASSERT(tuple->kind == Type_Tuple); + gbAllocator a = proc->module->allocator; + + Type *src_type = ssa_type(value); + bool is_ptr = is_type_pointer(src_type); + + ssaValue *v = ssa_add_local_generated(proc, tuple); + + if (is_ptr) { + Type *src = base_type(type_deref(src_type)); + Type *src_ptr = src_type; + GB_ASSERT(is_type_union(src)); + Type *dst_ptr = tuple->Tuple.variables[0]->type; + Type *dst = type_deref(dst_ptr); + + ssaValue *tag = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, value)); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + 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); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_conv(proc, value, dst_ptr); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } else { + Type *src = base_type(src_type); + GB_ASSERT(is_type_union(src)); + Type *dst = tuple->Tuple.variables[0]->type; + Type *dst_ptr = make_type_pointer(a, dst); + + ssaValue *tag = ssa_emit_union_tag_value(proc, value); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + // HACK(bill): This is probably not very efficient + ssaValue *union_copy = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, union_copy, value); + + 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); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr)); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } + return ssa_emit_load(proc, v); +} + + +isize ssa_type_info_index(CheckerInfo *info, Type *type) { + type = default_type(type); + + isize entry_index = -1; + HashKey key = hash_pointer(type); + isize *found_entry_index = map_isize_get(&info->type_info_map, key); + if (found_entry_index) { + entry_index = *found_entry_index; + } + if (entry_index < 0) { + // NOTE(bill): Do manual search + // TODO(bill): This is O(n) and can be very slow + for_array(i, info->type_info_map.entries){ + MapIsizeEntry *e = &info->type_info_map.entries.e[i]; + Type *prev_type = cast(Type *)e->key.ptr; + if (are_types_identical(prev_type, type)) { + entry_index = e->value; + // NOTE(bill): Add it to the search map + map_isize_set(&info->type_info_map, key, entry_index); + break; + } + } + } + + if (entry_index < 0) { + compiler_error("Type_Info for `%s` could not be found", type_to_string(type)); + } + return entry_index; +} + +ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) { + ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + type = default_type(type); + + i32 entry_index = ssa_type_info_index(info, type); + + // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); + + return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index)); +} + + + +ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) { + ast_node(be, BinaryExpr, expr); +#if 0 + ssaBlock *true_ = ssa_add_block(proc, NULL, "logical.cmp.true"); + ssaBlock *false_ = ssa_add_block(proc, NULL, "logical.cmp.false"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + ssaValue *result = ssa_add_local_generated(proc, t_bool); + ssa_build_cond(proc, expr, true_, false_); + + proc->curr_block = true_; + ssa_emit_store(proc, result, v_true); + ssa_emit_jump(proc, done); + + proc->curr_block = false_; + ssa_emit_store(proc, result, v_false); + ssa_emit_jump(proc, done); + + proc->curr_block = done; + + return ssa_emit_load(proc, result); +#else + ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + Type *type = type_of_expr(proc->module->info, expr); + type = default_type(type); + + ssaValue *short_circuit = NULL; + if (be->op.kind == Token_CmpAnd) { + ssa_build_cond(proc, be->left, rhs, done); + short_circuit = v_false; + } else if (be->op.kind == Token_CmpOr) { + ssa_build_cond(proc, be->left, done, rhs); + short_circuit = v_true; + } + + if (rhs->preds.count == 0) { + proc->curr_block = done; + return short_circuit; + } + + if (done->preds.count == 0) { + proc->curr_block = rhs; + return ssa_build_expr(proc, be->right); + } + + ssaValueArray edges = {0}; + array_init_reserve(&edges, proc->module->allocator, done->preds.count+1); + for_array(i, done->preds) { + array_add(&edges, short_circuit); + } + + proc->curr_block = rhs; + array_add(&edges, ssa_build_expr(proc, be->right)); + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type)); +#endif +} + + +void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + index = ssa_emit_conv(proc, index, t_int); + len = ssa_emit_conv(proc, len, t_int); + + ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 5); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, index, t_int); + // args[4] = ssa_emit_conv(proc, len, t_int); + + // ssa_emit_global_call(proc, "__bounds_check_error", args, 5); +} + +void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + + low = ssa_emit_conv(proc, low, t_int); + high = ssa_emit_conv(proc, high, t_int); + max = ssa_emit_conv(proc, max, t_int); + + ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 6); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, low, t_int); + // args[4] = ssa_emit_conv(proc, high, t_int); + // args[5] = ssa_emit_conv(proc, max, t_int); + + // if (!is_substring) { + // ssa_emit_global_call(proc, "__slice_expr_error", args, 6); + // } else { + // ssa_emit_global_call(proc, "__substring_expr_error", args, 5); + // } +} + + +//////////////////////////////////////////////////////////////// +// +// @Build +// +//////////////////////////////////////////////////////////////// + + +void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { + ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList); + tl->prev = proc->target_list; + tl->break_ = break_; + tl->continue_ = continue_; + tl->fallthrough_ = fallthrough_; + proc->target_list = tl; +} + +void ssa_pop_target_list(ssaProcedure *proc) { + proc->target_list = proc->target_list->prev; +} + + +void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) { + if (field->kind != Entity_TypeName) { + return; + } + String cn = field->token.string; + + isize len = parent.len + 1 + cn.len; + String child = {NULL, len}; + child.text = gb_alloc_array(m->allocator, u8, len); + + isize i = 0; + gb_memmove(child.text+i, parent.text, parent.len); + i += parent.len; + child.text[i++] = '.'; + gb_memmove(child.text+i, cn.text, cn.len); + + map_string_set(&m->type_names, hash_pointer(field->type), child); + ssa_gen_global_type_name(m, field, child); +} + +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { + ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type); + ssa_module_add_value(m, e, t); + map_ssa_value_set(&m->members, hash_string(name), t); + + Type *bt = base_type(e->type); + if (bt->kind == Type_Record) { + TypeRecord *s = &bt->Record; + for (isize j = 0; j < s->other_field_count; j++) { + ssa_mangle_sub_type_name(m, s->other_fields[j], name); + } + } + + if (is_type_union(bt)) { + TypeRecord *s = &bt->Record; + // NOTE(bill): Zeroth entry is null (for `match type` stmts) + for (isize j = 1; j < s->field_count; j++) { + ssa_mangle_sub_type_name(m, s->fields[j], name); + } + } +} + + + + +void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) { + ssaBlock *b = ssa_add_block(proc, NULL, "defer"); + // NOTE(bill): The prev block may defer injection before it's terminator + ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block); + if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) { + ssa_emit_jump(proc, b); + } + proc->curr_block = b; + ssa_emit_comment(proc, str_lit("defer")); + if (d.kind == ssaDefer_Node) { + ssa_build_stmt(proc, d.stmt); + } else if (d.kind == ssaDefer_Instr) { + // NOTE(bill): Need to make a new copy + ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue)); + ssa_emit(proc, instr); + } +} + + + +ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) { + ssaValue **value = map_ssa_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); + return *value; +} + +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_ssa_value_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) { + expr = unparen_expr(expr); + switch (expr->kind) { + case_ast_node(bl, BasicLit, expr); + GB_PANIC("Non-constant basic literal"); + case_end; + + case_ast_node(i, Ident, expr); + Entity *e = *map_entity_get(&proc->module->info->uses, hash_pointer(expr)); + if (e->kind == Entity_Builtin) { + Token token = ast_node_token(expr); + GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n" + "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name), + LIT(token.pos.file), token.pos.line, token.pos.column); + return NULL; + } 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_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id)); + } + + ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + ssaValue *v = *found; + if (v->kind == ssaValue_Proc) { + return v; + } + // if (e->kind == Entity_Variable && e->Variable.param) { + // return v; + // } + return ssa_emit_load(proc, v); + } + return NULL; + case_end; + + case_ast_node(re, RunExpr, expr); + // TODO(bill): Run Expression + return ssa_build_single_expr(proc, re->expr, tv); + case_end; + + case_ast_node(de, DerefExpr, expr); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + TypeAndValue *tav = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT(tav != NULL); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: + return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer + + case Token_Maybe: + return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); + + case Token_Add: + return ssa_build_expr(proc, ue->expr); + + case Token_Sub: // NOTE(bill): -`x` == 0 - `x` + return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type); + + case Token_Not: // Boolean not + case Token_Xor: { // Bitwise not + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *left = ssa_build_expr(proc, ue->expr); + ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1)); + return ssa_emit_arith(proc, ue->op.kind, + left, right, + tv->type); + } break; + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + ssaValue *left = ssa_build_expr(proc, be->left); + Type *type = default_type(tv->type); + + switch (be->op.kind) { + case Token_Add: + case Token_Sub: + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_Or: + case Token_Xor: + case Token_AndNot: + case Token_Shl: + case Token_Shr: { + ssaValue *right = ssa_build_expr(proc, be->right); + return ssa_emit_arith(proc, be->op.kind, left, right, type); + } + + + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_LtEq: + case Token_Gt: + case Token_GtEq: { + ssaValue *right = ssa_build_expr(proc, be->right); + ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right); + return ssa_emit_conv(proc, cmp, type); + } break; + + case Token_CmpAnd: + case Token_CmpOr: + return ssa_emit_logical_binary_expr(proc, expr); + + case Token_as: + ssa_emit_comment(proc, str_lit("cast - as")); + return ssa_emit_conv(proc, left, type); + + case Token_transmute: + ssa_emit_comment(proc, str_lit("cast - transmute")); + return ssa_emit_transmute(proc, left, type); + + case Token_down_cast: + ssa_emit_comment(proc, str_lit("cast - down_cast")); + return ssa_emit_down_cast(proc, left, type); + + case Token_union_cast: + ssa_emit_comment(proc, str_lit("cast - union_cast")); + return ssa_emit_union_cast(proc, left, type); + + default: + GB_PANIC("Invalid binary expression"); + break; + } + case_end; + + case_ast_node(pl, ProcLit, expr); + // NOTE(bill): Generate a new name + // parent$count + isize name_len = proc->name.len + 1 + 8 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count); + String name = make_string(name_text, name_len-1); + + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, NULL, type, pl->type, pl->body, name); + + value->Proc.tags = pl->tags; + + array_add(&proc->children, &value->Proc); + ssa_build_proc(value, proc); + + return value; + case_end; + + + case_ast_node(cl, CompoundLit, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + + case_ast_node(ce, CallExpr, expr); + AstNode *p = unparen_expr(ce->proc); + if (p->kind == AstNode_Ident) { + Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(p)); + if (found && (*found)->kind == Entity_Builtin) { + Entity *e = *found; + switch (e->Builtin.id) { + case BuiltinProc_type_info: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0])); + return ssa_type_info(proc, t); + } break; + case BuiltinProc_type_info_of_val: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0])); + return ssa_type_info(proc, t); + } break; + + case BuiltinProc_new: { + ssa_emit_comment(proc, str_lit("new")); + // new :: proc(Type) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args.e[0]); + Type *ptr_type = make_type_pointer(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = ssa_make_const_int(allocator, s); + args[1] = ssa_make_const_int(allocator, a); + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + ssaValue *v = ssa_emit_conv(proc, call, ptr_type); + return v; + } break; + + case BuiltinProc_new_slice: { + ssa_emit_comment(proc, str_lit("new_slice")); + // new_slice :: proc(Type, len: int[, cap: int]) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args.e[0]); + Type *ptr_type = make_type_pointer(allocator, type); + Type *slice_type = make_type_slice(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue *elem_size = ssa_make_const_int(allocator, s); + ssaValue *elem_align = ssa_make_const_int(allocator, a); + + ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[1]), t_int); + ssaValue *cap = len; + if (ce->args.count == 3) { + cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[2]), t_int); + } + + ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, len, cap, false); + + ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = slice_size; + args[1] = elem_align; + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + + ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, ptr); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_assert: { + ssa_emit_comment(proc, str_lit("assert")); + ssaValue *cond = ssa_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_boolean(ssa_type(cond))); + + cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false); + ssaBlock *err = ssa_add_block(proc, NULL, "builtin.assert.err"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done"); + + ssa_emit_if(proc, cond, err, done); + proc->curr_block = err; + + // TODO(bill): Cleanup allocations here + Token token = ast_node_token(ce->args.e[0]); + TokenPos pos = token.pos; + gbString expr = expr_to_string(ce->args.e[0]); + isize expr_len = gb_string_length(expr); + String expr_str = {0}; + expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1); + expr_str.len = expr_len; + gb_string_free(expr); + + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = ssa_make_const_string(proc->module->allocator, expr_str); + ssa_emit_global_call(proc, "__assert", args, 4); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return NULL; + } break; + + case BuiltinProc_panic: { + ssa_emit_comment(proc, str_lit("panic")); + ssaValue *msg = ssa_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_string(ssa_type(msg))); + + Token token = ast_node_token(ce->args.e[0]); + TokenPos pos = token.pos; + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = msg; + ssa_emit_global_call(proc, "__assert", args, 4); + + return NULL; + } break; + + + case BuiltinProc_copy: { + ssa_emit_comment(proc, str_lit("copy")); + // copy :: proc(dst, src: []Type) -> int + AstNode *dst_node = ce->args.e[0]; + AstNode *src_node = ce->args.e[1]; + ssaValue *dst_slice = ssa_build_expr(proc, dst_node); + ssaValue *src_slice = ssa_build_expr(proc, src_node); + Type *slice_type = base_type(ssa_type(dst_slice)); + GB_ASSERT(slice_type->kind == Type_Slice); + Type *elem_type = slice_type->Slice.elem; + i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + + + ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr); + ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr); + + ssaValue *len_dst = ssa_slice_len(proc, dst_slice); + ssaValue *len_src = ssa_slice_len(proc, src_slice); + + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src); + ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src); + + ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem); + ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = dst; + args[1] = src; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + return len; + } break; + case BuiltinProc_append: { + ssa_emit_comment(proc, str_lit("append")); + // append :: proc(s: ^[]Type, item: Type) -> bool + AstNode *sptr_node = ce->args.e[0]; + AstNode *item_node = ce->args.e[1]; + ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node); + ssaValue *slice = ssa_emit_load(proc, slice_ptr); + + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *cap = ssa_slice_cap(proc, slice); + + Type *elem_type = type_deref(ssa_type(elem)); + + ssaValue *item_value = ssa_build_expr(proc, item_node); + item_value = ssa_emit_conv(proc, item_value, elem_type); + + ssaValue *item = ssa_add_local_generated(proc, elem_type); + ssa_emit_store(proc, item, item_value); + + + // NOTE(bill): Check if can append is possible + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap); + ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done"); + + ssa_emit_if(proc, cond, able, done); + proc->curr_block = able; + + // Add new slice item + i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size); + + ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len); + offset = ssa_emit_conv(proc, offset, t_rawptr); + + item = ssa_emit_ptr_offset(proc, item, v_zero); + item = ssa_emit_conv(proc, item, t_rawptr); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = offset; + args[1] = item; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + // Increment slice length + ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int); + ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1); + ssa_emit_store(proc, gep, new_len); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit_conv(proc, cond, t_bool); + } break; + + case BuiltinProc_swizzle: { + ssa_emit_comment(proc, str_lit("swizzle")); + ssaValue *vector = ssa_build_expr(proc, ce->args.e[0]); + isize index_count = ce->args.count-1; + if (index_count == 0) { + return vector; + } + + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + isize index = 0; + for_array(i, ce->args) { + if (i == 0) continue; + TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args.e[i]); + GB_ASSERT(is_type_integer(tv->type)); + GB_ASSERT(tv->value.kind == ExactValue_Integer); + indices[index++] = cast(i32)tv->value.value_integer; + } + + return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count)); + + } break; + +#if 0 + case BuiltinProc_ptr_offset: { + ssa_emit_comment(proc, str_lit("ptr_offset")); + ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *offset = ssa_build_expr(proc, ce->args.e[1]); + return ssa_emit_ptr_offset(proc, ptr, offset); + } break; + + case BuiltinProc_ptr_sub: { + ssa_emit_comment(proc, str_lit("ptr_sub")); + ssaValue *ptr_a = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *ptr_b = ssa_build_expr(proc, ce->args.e[1]); + Type *ptr_type = base_type(ssa_type(ptr_a)); + GB_ASSERT(ptr_type->kind == Type_Pointer); + isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem); + + ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int); + if (elem_size > 1) { + ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size); + v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int); + } + + return v; + } break; +#endif + + case BuiltinProc_slice_ptr: { + ssa_emit_comment(proc, str_lit("slice_ptr")); + ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *len = ssa_build_expr(proc, ce->args.e[1]); + ssaValue *cap = len; + + len = ssa_emit_conv(proc, len, t_int); + + if (ce->args.count == 3) { + cap = ssa_build_expr(proc, ce->args.e[2]); + cap = ssa_emit_conv(proc, cap, t_int); + } + + + Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr))); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_min: { + ssa_emit_comment(proc, str_lit("min")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *y = ssa_build_expr(proc, ce->args.e[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_max: { + ssa_emit_comment(proc, str_lit("max")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *y = ssa_build_expr(proc, ce->args.e[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_abs: { + ssa_emit_comment(proc, str_lit("abs")); + gbAllocator a = proc->module->allocator; + + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + Type *original_type = ssa_type(x); + Type *t = original_type; + i64 sz = type_size_of(proc->module->sizes, a, t); + GB_ASSERT(is_type_integer(t) || is_type_float(t)); + if (is_type_float(t)) { + if (sz == 4) { + t = t_i32; + } else if (sz == 8) { + t = t_i64; + } else { + GB_PANIC("unknown float type for `abs`"); + } + + x = ssa_emit_bitcast(proc, x, t); + } + + /* + NOTE(bill): See Hacker's Delight, section 2-4. + m := x >> (int_size-1) + b := x ^ m + return b - m + */ + + ssaValue *m = ssa_emit_arith(proc, Token_Shr, + x, + ssa_make_value_constant(a, t, make_exact_value_integer(sz-1)), + t); + ssaValue *b = ssa_emit_arith(proc, Token_Xor, x, m, t); + ssaValue *v = ssa_emit_arith(proc, Token_Sub, b, m, t); + + if (is_type_float(t)) { + v = ssa_emit_bitcast(proc, v, original_type); + } + return v; + } break; + + case BuiltinProc_enum_to_string: { + ssa_emit_comment(proc, str_lit("enum_to_string")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + Type *t = ssa_type(x); + ssaValue *ti = ssa_type_info(proc, t); + + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2); + args[0] = ti; + args[1] = ssa_emit_conv(proc, x, t_i64); + return ssa_emit_global_call(proc, "__enum_to_string", args, 2); + } break; + } + } + } + + + // NOTE(bill): Regular call + ssaValue *value = ssa_build_expr(proc, ce->proc); + Type *proc_type_ = base_type(ssa_type(value)); + GB_ASSERT(proc_type_->kind == Type_Proc); + TypeProc *type = &proc_type_->Proc; + + isize arg_index = 0; + + isize arg_count = 0; + for_array(i, ce->args) { + AstNode *a = ce->args.e[i]; + Type *at = base_type(type_of_expr(proc->module->info, a)); + if (at->kind == Type_Tuple) { + arg_count += at->Tuple.variable_count; + } else { + arg_count++; + } + } + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count); + bool variadic = proc_type_->Proc.variadic; + bool vari_expand = ce->ellipsis.pos.line != 0; + + for_array(i, ce->args) { + ssaValue *a = ssa_build_expr(proc, ce->args.e[i]); + Type *at = ssa_type(a); + if (at->kind == Type_Tuple) { + for (isize i = 0; i < at->Tuple.variable_count; i++) { + Entity *e = at->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, a, i); + args[arg_index++] = v; + } + } else { + args[arg_index++] = a; + } + } + + TypeTuple *pt = &type->params->Tuple; + + if (variadic) { + isize i = 0; + for (; i < type->param_count-1; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + if (!vari_expand) { + Type *variadic_type = pt->variables[i]->type; + GB_ASSERT(is_type_slice(variadic_type)); + variadic_type = base_type(variadic_type)->Slice.elem; + for (; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], variadic_type); + } + } + } else { + for (isize i = 0; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + } + + if (variadic && !vari_expand) { + ssa_emit_comment(proc, str_lit("variadic call argument generation")); + gbAllocator allocator = proc->module->allocator; + Type *slice_type = pt->variables[type->param_count-1]->type; + Type *elem_type = base_type(slice_type)->Slice.elem; + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + isize slice_len = arg_count+1 - type->param_count; + + if (slice_len > 0) { + ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len)); + + for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) { + ssaValue *addr = ssa_emit_array_epi(proc, base_array, j); + ssa_emit_store(proc, addr, args[i]); + } + + ssaValue *base_elem = ssa_emit_array_epi(proc, base_array, 0); + ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, slice_elem, base_elem); + ssaValue *len = ssa_make_const_int(allocator, slice_len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len); + } + + if (args[0]->kind == ssaValue_Constant) { + ssaValueConstant *c = &args[0]->Constant; + gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind); + } + + arg_count = type->param_count; + args[arg_count-1] = ssa_emit_load(proc, slice); + } + + return ssa_emit_call(proc, value, args, arg_count); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(se, SliceExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(ie, IndexExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + } + + GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); + return NULL; +} + + +ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { + expr = unparen_expr(expr); + + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT_NOT_NULL(tv); + + if (tv->value.kind != ExactValue_Invalid) { + return ssa_add_module_constant(proc->module, tv->type, tv->value); + } + + ssaValue *value = NULL; + if (tv->mode == Addressing_Variable) { + value = ssa_addr_load(proc, ssa_build_addr(proc, expr)); + } else { + value = ssa_build_single_expr(proc, expr, tv); + } + + return value; +} + +ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) { + GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); + String name = e->token.string; + Entity *parent = e->using_parent; + Selection sel = lookup_field(proc->module->allocator, parent->type, name, false); + GB_ASSERT(sel.entity != NULL); + ssaValue **pv = map_ssa_value_get(&proc->module->values, hash_pointer(parent)); + ssaValue *v = NULL; + if (pv != NULL) { + v = *pv; + } else { + v = ssa_build_addr(proc, e->using_expr).addr; + } + GB_ASSERT(v != NULL); + ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel); + map_ssa_value_set(&proc->module->values, hash_pointer(e), var); + return var; +} + +bool ssa_is_elem_const(ssaModule *m, AstNode *elem, Type *elem_type) { + if (base_type(elem_type) == t_any) { + return false; + } + if (elem->kind == AstNode_FieldValue) { + elem = elem->FieldValue.value; + } + TypeAndValue *tav = type_and_value_of_expression(m->info, elem); + GB_ASSERT(tav != NULL); + return tav->value.kind != ExactValue_Invalid; +} + +ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { + switch (expr->kind) { + case_ast_node(i, Ident, expr); + if (ssa_is_blank_ident(expr)) { + ssaAddr val = {0}; + return val; + } + + Entity *e = entity_of_ident(proc->module->info, expr); + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + + GB_ASSERT(e->kind != Entity_Constant); + + ssaValue *v = NULL; + ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + v = *found; + } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + v = ssa_add_using_variable(proc, e); + } else if (e->kind == Entity_ImplicitValue) { + // TODO(bill): Should a copy be made? + v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id); + } + + if (v == NULL) { + GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind])); + } + + return ssa_make_addr(v, expr); + case_end; + + case_ast_node(pe, ParenExpr, expr); + return ssa_build_addr(proc, unparen_expr(expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + ssa_emit_comment(proc, str_lit("SelectorExpr")); + String selector = unparen_expr(se->selector)->Ident.string; + Type *type = base_type(type_of_expr(proc->module->info, se->expr)); + + if (type == t_invalid) { + // NOTE(bill): Imports + Entity *imp = entity_of_ident(proc->module->info, se->expr); + if (imp != NULL) { + GB_ASSERT(imp->kind == Entity_ImportName); + } + return ssa_build_addr(proc, unparen_expr(se->selector)); + } else { + Selection sel = lookup_field(proc->module->allocator, type, selector, false); + GB_ASSERT(sel.entity != NULL); + + ssaValue *a = ssa_build_addr(proc, se->expr).addr; + a = ssa_emit_deep_field_gep(proc, type, a, sel); + return ssa_make_addr(a, expr); + } + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: { + return ssa_build_addr(proc, ue->expr); + } + default: + GB_PANIC("Invalid unary expression for ssa_build_addr"); + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + switch (be->op.kind) { + case Token_as: { + ssa_emit_comment(proc, str_lit("Cast - as")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + case Token_transmute: { + ssa_emit_comment(proc, str_lit("Cast - transmute")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + default: + GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string)); + break; + } + case_end; + + case_ast_node(ie, IndexExpr, expr); + ssa_emit_comment(proc, str_lit("IndexExpr")); + Type *t = base_type(type_of_expr(proc->module->info, ie->expr)); + gbAllocator a = proc->module->allocator; + + + bool deref = is_type_pointer(t); + t = type_deref(t); + + ssaValue *using_addr = NULL; + if (!is_type_indexable(t)) { + // Using index expression + Entity *using_field = find_using_index_expr(t); + if (using_field != NULL) { + Selection sel = lookup_field(a, t, using_field->token.string, false); + ssaValue *e = ssa_build_addr(proc, ie->expr).addr; + using_addr = ssa_emit_deep_field_gep(proc, t, e, sel); + + t = using_field->type; + } + } + + + switch (t->kind) { + case Type_Vector: { + ssaValue *vector = NULL; + if (using_addr != NULL) { + vector = using_addr; + } else { + vector = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + vector = ssa_emit_load(proc, vector); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr_vector(vector, index, expr); + } break; + + case Type_Array: { + ssaValue *array = NULL; + if (using_addr != NULL) { + array = using_addr; + } else { + array = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + array = ssa_emit_load(proc, array); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *elem = ssa_emit_array_ep(proc, array, index); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr(elem, expr); + } break; + + case Type_Slice: { + ssaValue *slice = NULL; + if (using_addr != NULL) { + slice = ssa_emit_load(proc, using_addr); + } else { + slice = ssa_build_expr(proc, ie->expr); + if (deref) { + slice = ssa_emit_load(proc, slice); + } + } + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + ssaValue *v = ssa_emit_ptr_offset(proc, elem, index); + return ssa_make_addr(v, expr); + + } break; + + case Type_Basic: { // Basic_string + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr)); + ssaValue *str; + ssaValue *elem; + ssaValue *len; + ssaValue *index; + + if (using_addr != NULL) { + str = ssa_emit_load(proc, using_addr); + } else { + str = ssa_build_expr(proc, ie->expr); + if (deref) { + str = ssa_emit_load(proc, str); + } + } + elem = ssa_string_elem(proc, str); + len = ssa_string_len(proc, str); + + index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + + return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr); + } break; + } + case_end; + + case_ast_node(se, SliceExpr, expr); + ssa_emit_comment(proc, str_lit("SliceExpr")); + gbAllocator a = proc->module->allocator; + ssaValue *low = v_zero; + ssaValue *high = NULL; + ssaValue *max = NULL; + + if (se->low != NULL) low = ssa_build_expr(proc, se->low); + if (se->high != NULL) high = ssa_build_expr(proc, se->high); + if (se->triple_indexed) max = ssa_build_expr(proc, se->max); + ssaValue *addr = ssa_build_addr(proc, se->expr).addr; + ssaValue *base = ssa_emit_load(proc, addr); + Type *type = base_type(ssa_type(base)); + + if (is_type_pointer(type)) { + type = type_deref(type); + addr = base; + base = ssa_emit_load(proc, base); + } + + // TODO(bill): Cleanup like mad! + + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + + if (high == NULL) high = ssa_slice_len(proc, base); + if (max == NULL) max = ssa_slice_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_slice_elem(proc, base); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Array: { + Type *slice_type = make_type_slice(a, type->Array.elem); + + if (high == NULL) high = ssa_array_len(proc, base); + if (max == NULL) max = ssa_array_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_array_elem(proc, addr); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Basic: { + GB_ASSERT(type == t_string); + if (high == NULL) { + high = ssa_string_len(proc, base); + } + + ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true); + + ssaValue *elem, *len; + len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + + elem = ssa_string_elem(proc, base); + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + + return ssa_make_addr(str, expr); + } break; + } + + GB_PANIC("Unknown slicable type"); + case_end; + + case_ast_node(de, DerefExpr, expr); + // TODO(bill): Is a ptr copy needed? + ssaValue *addr = ssa_build_expr(proc, de->expr); + addr = ssa_emit_ptr_offset(proc, addr, v_zero); + return ssa_make_addr(addr, expr); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + ssa_emit_comment(proc, str_lit("DemaybeExpr")); + ssaValue *maybe = ssa_build_expr(proc, de->expr); + Type *t = default_type(type_of_expr(proc->module->info, expr)); + GB_ASSERT(is_type_tuple(t)); + + ssaValue *result = ssa_add_local_generated(proc, t); + ssa_emit_store(proc, result, maybe); + + return ssa_make_addr(result, expr); + case_end; + + case_ast_node(ce, CallExpr, expr); + ssaValue *e = ssa_build_expr(proc, expr); + ssaValue *v = ssa_add_local_generated(proc, ssa_type(e)); + ssa_emit_store(proc, v, e); + return ssa_make_addr(v, expr); + case_end; + + + case_ast_node(cl, CompoundLit, expr); + ssa_emit_comment(proc, str_lit("CompoundLit")); + Type *type = type_of_expr(proc->module->info, expr); + Type *bt = base_type(type); + ssaValue *v = ssa_add_local_generated(proc, type); + + Type *et = NULL; + switch (bt->kind) { + case Type_Vector: et = bt->Vector.elem; break; + case Type_Array: et = bt->Array.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + } + + switch (bt->kind) { + default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; + + case Type_Vector: { + ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + for_array(index, cl->elems) { + AstNode *elem = cl->elems.e[index]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_elem = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_elem); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_elem, et); + ssaValue *i = ssa_make_const_int(proc->module->allocator, index); + result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i)); + } + + if (cl->elems.count == 1 && bt->Vector.count > 1) { + isize index_count = bt->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count)); + ssa_emit_store(proc, v, sv); + return ssa_make_addr(v, expr); + } + ssa_emit_store(proc, v, result); + } break; + + case Type_Record: { + GB_ASSERT(is_type_struct(bt)); + TypeRecord *st = &bt->Record; + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(field_index, cl->elems) { + AstNode *elem = cl->elems.e[field_index]; + + ssaValue *field_expr = NULL; + Entity *field = NULL; + isize index = field_index; + + if (elem->kind == AstNode_FieldValue) { + ast_node(fv, FieldValue, elem); + Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false); + index = sel.index.e[0]; + elem = fv->value; + } else { + TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem); + Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false); + index = sel.index.e[0]; + } + + field = st->fields[index]; + if (ssa_is_elem_const(proc->module, elem, field->type)) { + continue; + } + + field_expr = ssa_build_expr(proc, elem); + + GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple); + + Type *ft = field->type; + ssaValue *fv = ssa_emit_conv(proc, field_expr, ft); + ssaValue *gep = ssa_emit_struct_ep(proc, v, index); + ssa_emit_store(proc, gep, fv); + } + } + } break; + case Type_Array: { + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, et); + ssaValue *gep = ssa_emit_array_epi(proc, v, i); + ssa_emit_store(proc, gep, ev); + } + } + } break; + case Type_Slice: { + if (cl->elems.count > 0) { + Type *elem_type = bt->Slice.elem; + Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type); + Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type); + ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + GB_ASSERT(slice->kind == ssaValue_ConstantSlice); + + ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type); + ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i)); + ssa_emit_store(proc, offset, ev); + } + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1); + + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + } + } break; + } + + return ssa_make_addr(v, expr); + case_end; + + + } + + TokenPos token_pos = ast_node_token(expr).pos; + GB_PANIC("Unexpected address expression\n" + "\tAstNode: %.*s @ " + "%.*s(%td:%td)\n", + LIT(ast_node_strings[expr->kind]), + LIT(token_pos.file), token_pos.line, token_pos.column); + + + return ssa_make_addr(NULL, NULL); +} + +void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) { + ssaValue *old_value = ssa_addr_load(proc, lhs); + Type *type = ssa_type(old_value); + + ssaValue *change = value; + if (is_type_pointer(type) && is_type_integer(ssa_type(value))) { + change = ssa_emit_conv(proc, value, default_type(ssa_type(value))); + } else { + change = ssa_emit_conv(proc, value, type); + } + ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type); + ssa_addr_store(proc, lhs, new_value); +} + +void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + ssa_build_cond(proc, pe->expr, true_block, false_block); + return; + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + ssa_build_cond(proc, ue->expr, false_block, true_block); + return; + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and"); + ssa_build_cond(proc, be->left, block, false_block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } else if (be->op.kind == Token_CmpOr) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or"); + ssa_build_cond(proc, be->left, true_block, block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } + case_end; + } + + ssaValue *expr = ssa_build_expr(proc, cond); + expr = ssa_emit_conv(proc, expr, t_bool); + ssa_emit_if(proc, expr, true_block, false_block); +} + + + + +void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) { + for_array(i, stmts) { + ssa_build_stmt(proc, stmts.e[i]); + } +} + +void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node); +void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + + if (node->stmt_state_flags != 0) { + u32 in = node->stmt_state_flags; + u32 out = proc->module->stmt_state_flags; + + if (in & StmtStateFlag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & StmtStateFlag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + + proc->module->stmt_state_flags = out; + } + + ssa_build_stmt_internal(proc, node); + + proc->module->stmt_state_flags = prev_stmt_state_flags; +} + +void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) { + switch (node->kind) { + case_ast_node(bs, EmptyStmt, node); + case_end; + + case_ast_node(us, UsingStmt, node); + AstNode *decl = unparen_expr(us->node); + if (decl->kind == AstNode_VarDecl) { + ssa_build_stmt(proc, decl); + } + case_end; + + case_ast_node(vd, VarDecl, node); + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + if (vd->values.count == 0) { // declared and zero-initialized + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, true); + } + } + } else { // Tuple(s) + Array(ssaAddr) lvals; + ssaValueArray inits; + array_init_reserve(&lvals, m->tmp_allocator, vd->names.count); + array_init_reserve(&inits, m->tmp_allocator, vd->names.count); + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + ssaAddr lval = ssa_make_addr(NULL, NULL); + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, false); + lval = ssa_build_addr(proc, name); + } + + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + ssaValue *init = ssa_build_expr(proc, vd->values.e[i]); + Type *t = ssa_type(init); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + + for_array(i, inits) { + if (lvals.e[i].addr == NULL) { + continue; + } + ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i])); + ssa_addr_store(proc, lvals.e[i], v); + } + } + + gb_temp_arena_memory_end(tmp); + case_end; + + case_ast_node(pd, ProcDecl, node); + if (pd->body != NULL) { + CheckerInfo *info = proc->module->info; + + Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + + if (map_entity_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + break; + } + + // NOTE(bill): Generate a new name + // parent.name-guid + String original_name = pd->name->Ident.string; + String pd_name = original_name; + if (pd->link_name.len > 0) { + pd_name = pd->link_name; + } + + isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->children.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid); + String name = make_string(name_text, name_len-1); + + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + value->Proc.parent = proc; + + ssa_module_add_value(proc->module, e, value); + array_add(&proc->children, &value->Proc); + array_add(&proc->module->procs_to_generate, value); + } else { + CheckerInfo *info = proc->module->info; + + Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + // FFI - Foreign function interace + String original_name = pd->name->Ident.string; + String name = original_name; + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + + ssa_module_add_value(proc->module, e, value); + ssa_build_proc(value, proc); + + if (value->Proc.tags & ProcTag_foreign) { + HashKey key = hash_string(name); + ssaValue **prev_value = map_ssa_value_get(&proc->module->members, key); + if (prev_value == NULL) { + // NOTE(bill): Don't do mutliple declarations in the IR + map_ssa_value_set(&proc->module->members, key, value); + } + } else { + array_add(&proc->children, &value->Proc); + } + } + case_end; + + case_ast_node(td, TypeDecl, node); + + // NOTE(bill): Generate a new name + // parent_proc.name-guid + String td_name = td->name->Ident.string; + isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->module->members.entries.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid); + String name = make_string(name_text, name_len-1); + + Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(td->name)); + GB_ASSERT(found != NULL); + Entity *e = *found; + ssaValue *value = ssa_make_value_type_name(proc->module->allocator, + name, e->type); + map_string_set(&proc->module->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(proc->module, e, name); + case_end; + + case_ast_node(ids, IncDecStmt, node); + ssa_emit_comment(proc, str_lit("IncDecStmt")); + TokenKind op = ids->op.kind; + if (op == Token_Increment) { + op = Token_Add; + } else if (op == Token_Decrement) { + op = Token_Sub; + } + ssaAddr lval = ssa_build_addr(proc, ids->expr); + ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval)); + ssa_build_assign_op(proc, lval, one, op); + + case_end; + + case_ast_node(as, AssignStmt, node); + ssa_emit_comment(proc, str_lit("AssignStmt")); + + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + switch (as->op.kind) { + case Token_Eq: { + Array(ssaAddr) lvals; + array_init(&lvals, m->tmp_allocator); + + for_array(i, as->lhs) { + AstNode *lhs = as->lhs.e[i]; + ssaAddr lval = {0}; + if (!ssa_is_blank_ident(lhs)) { + lval = ssa_build_addr(proc, lhs); + } + array_add(&lvals, lval); + } + + if (as->lhs.count == as->rhs.count) { + if (as->lhs.count == 1) { + AstNode *rhs = as->rhs.e[0]; + ssaValue *init = ssa_build_expr(proc, rhs); + ssa_addr_store(proc, lvals.e[0], init); + } else { + ssaValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]); + array_add(&inits, init); + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals.e[i], inits.e[i]); + } + } + } else { + ssaValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]); + Type *t = ssa_type(init); + // TODO(bill): refactor for code reuse as this is repeated a bit + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals.e[i], inits.e[i]); + } + } + + } break; + + default: { + // NOTE(bill): Only 1 += 1 is allowed, no tuples + // +=, -=, etc + i32 op = cast(i32)as->op.kind; + op += Token_Add - Token_AddEq; // Convert += to + + ssaAddr lhs = ssa_build_addr(proc, as->lhs.e[0]); + ssaValue *value = ssa_build_expr(proc, as->rhs.e[0]); + ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op); + } break; + } + + gb_temp_arena_memory_end(tmp); + case_end; + + case_ast_node(es, ExprStmt, node); + // NOTE(bill): No need to use return value + ssa_build_expr(proc, es->expr); + case_end; + + case_ast_node(bs, BlockStmt, node); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, bs->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + case_ast_node(ds, DeferStmt, node); + ssa_emit_comment(proc, str_lit("DeferStmt")); + isize scope_index = proc->scope_index; + if (ds->stmt->kind == AstNode_BlockStmt) { + scope_index--; + } + ssa_add_defer_node(proc, scope_index, ds->stmt); + case_end; + + case_ast_node(rs, ReturnStmt, node); + ssa_emit_comment(proc, str_lit("ReturnStmt")); + ssaValue *v = NULL; + TypeTuple *return_type_tuple = &proc->type->Proc.results->Tuple; + isize return_count = proc->type->Proc.result_count; + if (return_count == 0) { + // No return values + } else if (return_count == 1) { + Entity *e = return_type_tuple->variables[0]; + v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results.e[0]), e->type); + } else { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + ssaValueArray results; + array_init_reserve(&results, proc->module->tmp_allocator, return_count); + + for_array(res_index, rs->results) { + ssaValue *res = ssa_build_expr(proc, rs->results.e[res_index]); + Type *t = ssa_type(res); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, res, i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + + Type *ret_type = proc->type->Proc.results; + v = ssa_add_local_generated(proc, ret_type); + for_array(i, results) { + Entity *e = return_type_tuple->variables[i]; + ssaValue *res = ssa_emit_conv(proc, results.e[i], e->type); + ssaValue *field = ssa_emit_struct_ep(proc, v, i); + ssa_emit_store(proc, field, res); + } + + v = ssa_emit_load(proc, v); + + gb_temp_arena_memory_end(tmp); + } + ssa_emit_return(proc, v); + + case_end; + + case_ast_node(is, IfStmt, node); + ssa_emit_comment(proc, str_lit("IfStmt")); + if (is->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "if.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, is->init); + } + ssaBlock *then = ssa_add_block(proc, node, "if.then"); + ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later + ssaBlock *else_ = done; + if (is->else_stmt != NULL) { + else_ = ssa_add_block(proc, is->else_stmt, "if.else"); + } + + ssa_build_cond(proc, is->cond, then, else_); + proc->curr_block = then; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + + if (is->else_stmt != NULL) { + proc->curr_block = else_; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->else_stmt); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + } + proc->curr_block = done; + case_end; + + case_ast_node(fs, ForStmt, node); + ssa_emit_comment(proc, str_lit("ForStmt")); + if (fs->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "for.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, fs->init); + } + ssaBlock *body = ssa_add_block(proc, node, "for.body"); + ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later + + ssaBlock *loop = body; + + if (fs->cond != NULL) { + loop = ssa_add_block(proc, node, "for.loop"); + } + ssaBlock *cont = loop; + if (fs->post != NULL) { + cont = ssa_add_block(proc, node, "for.post"); + + } + ssa_emit_jump(proc, loop); + proc->curr_block = loop; + if (loop != body) { + ssa_build_cond(proc, fs->cond, body, done); + proc->curr_block = body; + } + + ssa_push_target_list(proc, done, cont, NULL); + + ssa_open_scope(proc); + ssa_build_stmt(proc, fs->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_pop_target_list(proc); + ssa_emit_jump(proc, cont); + + if (fs->post != NULL) { + proc->curr_block = cont; + ssa_build_stmt(proc, fs->post); + ssa_emit_jump(proc, loop); + } + + + proc->curr_block = done; + + case_end; + + case_ast_node(ms, MatchStmt, node); + ssa_emit_comment(proc, str_lit("MatchStmt")); + if (ms->init != NULL) { + ssa_build_stmt(proc, ms->init); + } + ssaValue *tag = v_true; + if (ms->tag != NULL) { + tag = ssa_build_expr(proc, ms->tag); + } + ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + AstNodeArray default_stmts = {0}; + ssaBlock *default_fall = NULL; + ssaBlock *default_block = NULL; + + ssaBlock *fall = NULL; + bool append_fall = false; + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts.e[i]; + ssaBlock *body = fall; + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + if (cc->list.count == 0) { + body = ssa_add_block(proc, clause, "match.dflt.body"); + } else { + body = ssa_add_block(proc, clause, "match.case.body"); + } + } + if (append_fall && body == fall) { + append_fall = false; + } + + fall = done; + if (i+1 < case_count) { + append_fall = true; + fall = ssa_add_block(proc, clause, "match.fall.body"); + } + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_fall = fall; + default_block = body; + continue; + } + + ssaBlock *next_cond = NULL; + for_array(j, cc->list) { + AstNode *expr = cc->list.e[j]; + next_cond = ssa_add_block(proc, clause, "match.case.next"); + + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr)); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + } + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + 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); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, default_fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + + case_ast_node(ms, TypeMatchStmt, node); + ssa_emit_comment(proc, str_lit("TypeMatchStmt")); + gbAllocator allocator = proc->module->allocator; + + ssaValue *parent = ssa_build_expr(proc, ms->tag); + bool is_union_ptr = false; + bool is_any = false; + GB_ASSERT(check_valid_type_match_type(ssa_type(parent), &is_union_ptr, &is_any)); + + ssaValue *tag_index = NULL; + ssaValue *union_data = NULL; + if (is_union_ptr) { + ssa_emit_comment(proc, str_lit("get union's tag")); + tag_index = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, parent)); + union_data = ssa_emit_conv(proc, parent, t_rawptr); + } + + ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first"); + ssa_emit_jump(proc, start_block); + proc->curr_block = start_block; + + ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + String tag_var_name = ms->var->Ident.string; + + AstNodeArray default_stmts = {0}; + ssaBlock *default_block = NULL; + + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts.e[i]; + ast_node(cc, CaseClause, clause); + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_block = ssa_add_block(proc, clause, "type-match.dflt.body"); + continue; + } + + + ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body"); + + Scope *scope = *map_scope_get(&proc->module->info->scopes, hash_pointer(clause)); + Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); + GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name)); + + ssaBlock *next_cond = NULL; + ssaValue *cond = NULL; + + if (is_union_ptr) { + Type *bt = type_deref(tag_var_entity->type); + ssaValue *index = NULL; + Type *ut = base_type(type_deref(ssa_type(parent))); + GB_ASSERT(ut->Record.kind == TypeRecord_Union); + for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { + Entity *f = ut->Record.fields[field_index]; + if (are_types_identical(f->type, bt)) { + index = ssa_make_const_int(allocator, field_index); + break; + } + } + GB_ASSERT(index != NULL); + + ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); + ssaValue *data_ptr = ssa_emit_conv(proc, union_data, tag_var_entity->type); + ssa_emit_store(proc, tag_var, data_ptr); + + cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index); + } else if (is_any) { + Type *type = tag_var_entity->type; + ssaValue *any_data = ssa_emit_struct_ev(proc, parent, 1); + ssaValue *data = ssa_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); + ssa_module_add_value(proc->module, tag_var_entity, data); + + ssaValue *any_ti = ssa_emit_struct_ev(proc, parent, 0); + ssaValue *case_ti = ssa_type_info(proc, type); + cond = ssa_emit_comp(proc, Token_CmpEq, any_ti, case_ti); + } else { + GB_PANIC("Invalid type for type match statement"); + } + + next_cond = ssa_add_block(proc, clause, "type-match.case.next"); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + 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); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + case_ast_node(bs, BranchStmt, node); + ssaBlock *block = NULL; + switch (bs->token.kind) { + case Token_break: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->break_; + } + break; + case Token_continue: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->continue_; + } + break; + case Token_fallthrough: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->fallthrough_; + } + break; + } + if (block != NULL) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block); + } + switch (bs->token.kind) { + case Token_break: ssa_emit_comment(proc, str_lit("break")); break; + case Token_continue: ssa_emit_comment(proc, str_lit("continue")); break; + case Token_fallthrough: ssa_emit_comment(proc, str_lit("fallthrough")); break; + } + ssa_emit_jump(proc, block); + ssa_emit_unreachable(proc); + case_end; + + + + case_ast_node(pa, PushAllocator, node); + ssa_emit_comment(proc, str_lit("PushAllocator")); + ssa_open_scope(proc); + + 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)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1); + ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + + case_ast_node(pa, PushContext, node); + ssa_emit_comment(proc, str_lit("PushContext")); + ssa_open_scope(proc); + + 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)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Procedure +// +//////////////////////////////////////////////////////////////// + +void ssa_number_proc_registers(ssaProcedure *proc) { + i32 reg_index = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + b->index = i; + for_array(j, b->instrs) { + ssaValue *value = b->instrs.e[j]; + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + continue; + } + value->index = reg_index; + reg_index++; + } + } +} + +void ssa_begin_procedure_body(ssaProcedure *proc) { + array_add(&proc->module->procs, proc); + + array_init(&proc->blocks, heap_allocator()); + array_init(&proc->defer_stmts, heap_allocator()); + array_init(&proc->children, heap_allocator()); + + proc->decl_block = ssa_add_block(proc, proc->type_expr, "decls"); + proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry"); + proc->curr_block = proc->entry_block; + + if (proc->type->Proc.params != NULL) { + TypeTuple *params = &proc->type->Proc.params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + ssaValue *param = ssa_add_param(proc, e); + array_add(&proc->params, param); + } + } +} + + +void ssa_end_procedure_body(ssaProcedure *proc) { + if (proc->type->Proc.result_count == 0) { + ssa_emit_return(proc, NULL); + } + + if (proc->curr_block->instrs.count == 0) { + ssa_emit_unreachable(proc); + } + + proc->curr_block = proc->decl_block; + ssa_emit_jump(proc, proc->entry_block); + + ssa_number_proc_registers(proc); +} + + +void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) { + if (parent == NULL) { + if (str_eq(proc->name, str_lit("main"))) { + ssa_emit_startup_runtime(proc); + } + } +} + +void ssa_build_proc(ssaValue *value, ssaProcedure *parent) { + ssaProcedure *proc = &value->Proc; + + proc->parent = parent; + + if (proc->entity != NULL) { + ssaModule *m = proc->module; + CheckerInfo *info = m->info; + Entity *e = proc->entity; + String filename = e->token.pos.file; + AstFile **found = map_ast_file_get(&info->files, hash_string(filename)); + GB_ASSERT(found != NULL); + AstFile *f = *found; + ssaDebugInfo *di_file = NULL; + + ssaDebugInfo **di_file_found = map_ssa_debug_info_get(&m->debug_info, hash_pointer(f)); + if (di_file_found) { + di_file = *di_file_found; + GB_ASSERT(di_file->kind == ssaDebugInfo_File); + } else { + di_file = ssa_add_debug_info_file(proc, f); + } + + ssa_add_debug_info_proc(proc, e, proc->name, di_file); + } + + if (proc->body != NULL) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + + if (proc->tags != 0) { + u32 in = proc->tags; + u32 out = proc->module->stmt_state_flags; + if (in & ProcTag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & ProcTag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + proc->module->stmt_state_flags = out; + } + + + ssa_begin_procedure_body(proc); + ssa_insert_code_before_proc(proc, parent); + ssa_build_stmt(proc, proc->body); + ssa_end_procedure_body(proc); + + proc->module->stmt_state_flags = prev_stmt_state_flags; + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Module +// +//////////////////////////////////////////////////////////////// + + + +void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) { + map_ssa_value_set(&m->values, hash_pointer(e), v); +} + +void ssa_init_module(ssaModule *m, Checker *c) { + // TODO(bill): Determine a decent size for the arena + isize token_count = c->parser->total_token_count; + isize arena_size = 4 * token_count * gb_size_of(ssaValue); + gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size); + gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size); + m->allocator = gb_arena_allocator(&m->arena); + m->tmp_allocator = gb_arena_allocator(&m->tmp_arena); + m->info = &c->info; + m->sizes = c->sizes; + + map_ssa_value_init(&m->values, heap_allocator()); + map_ssa_value_init(&m->members, heap_allocator()); + map_ssa_debug_info_init(&m->debug_info, heap_allocator()); + map_string_init(&m->type_names, heap_allocator()); + array_init(&m->procs, heap_allocator()); + array_init(&m->procs_to_generate, heap_allocator()); + + // Default states + m->stmt_state_flags = 0; + m->stmt_state_flags |= StmtStateFlag_bounds_check; + + { + // Add type info data + { + String name = str_lit(SSA_TYPE_INFO_DATA_NAME); + isize count = c->info.type_info_map.entries.count; + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + g->Global.is_private = true; + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + } + + // Type info member buffer + { + // NOTE(bill): Removes need for heap allocation by making it global memory + isize count = 0; + + for_array(entry_index, m->info->type_info_map.entries) { + MapIsizeEntry *entry = &m->info->type_info_map.entries.e[entry_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + + switch (t->kind) { + case Type_Record: + switch (t->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + count += t->Record.field_count; + } + break; + case Type_Tuple: + count += t->Tuple.variable_count; + break; + } + } + + String name = str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME); + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), + make_type_array(m->allocator, t_type_info_member, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + } + } + + { + ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit); + di->CompileUnit.file = m->info->files.entries.e[0].value; // Zeroth is the init file + di->CompileUnit.producer = str_lit("odin"); + + map_ssa_debug_info_set(&m->debug_info, hash_pointer(m), di); + } +} + +void ssa_destroy_module(ssaModule *m) { + map_ssa_value_destroy(&m->values); + map_ssa_value_destroy(&m->members); + map_string_destroy(&m->type_names); + map_ssa_debug_info_destroy(&m->debug_info); + array_free(&m->procs_to_generate); + gb_arena_free(&m->arena); +} + + + +//////////////////////////////////////////////////////////////// +// +// @Code Generation +// +//////////////////////////////////////////////////////////////// + + +bool ssa_gen_init(ssaGen *s, Checker *c) { + if (global_error_collector.count != 0) { + return false; + } + + isize tc = c->parser->total_token_count; + if (tc < 2) { + return false; + } + + ssa_init_module(&s->module, c); + s->module.generate_debug_info = false; + + // TODO(bill): generate appropriate output name + int pos = cast(int)string_extension_position(c->parser->init_fullpath); + gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text)); + if (err != gbFileError_None) { + return false; + } + + return true; +} + +void ssa_gen_destroy(ssaGen *s) { + ssa_destroy_module(&s->module); + gb_file_close(&s->output_file); +} + +String ssa_mangle_name(ssaGen *s, String path, String name) { + // NOTE(bill): prefix names not in the init scope + // TODO(bill): make robust and not just rely on the file's name + + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + AstFile *file = *map_ast_file_get(&info->files, hash_string(path)); + + char *str = gb_alloc_array(a, char, path.len+1); + gb_memmove(str, path.text, path.len); + str[path.len] = 0; + for (isize i = 0; i < path.len; i++) { + if (str[i] == '\\') { + str[i] = '/'; + } + } + + char const *base = gb_path_base_name(str); + char const *ext = gb_path_extension(base); + isize base_len = ext-1-base; + + isize max_len = base_len + 1 + 10 + 1 + name.len; + u8 *new_name = gb_alloc_array(a, u8, max_len); + isize new_name_len = gb_snprintf( + cast(char *)new_name, max_len, + "%.*s-%u.%.*s", + cast(int)base_len, base, + file->id, + LIT(name)); + + return make_string(new_name, new_name_len-1); +} + +ssaValue *ssa_get_type_info_ptr(ssaProcedure *proc, ssaValue *type_info_data, Type *type) { + i32 index = cast(i32)ssa_type_info_index(proc->module->info, type); + // gb_printf_err("%d %s\n", index, type_to_string(type)); + return ssa_emit_array_epi(proc, type_info_data, index); +} + +ssaValue *ssa_type_info_member_offset(ssaProcedure *proc, ssaValue *data, isize count, i32 *index) { + ssaValue *offset = ssa_emit_array_epi(proc, data, *index); + *index += count; + return offset; +} + +void ssa_gen_tree(ssaGen *s) { + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + + if (v_zero == NULL) { + v_zero = ssa_make_const_int (m->allocator, 0); + v_one = ssa_make_const_int (m->allocator, 1); + v_zero32 = ssa_make_const_i32 (m->allocator, 0); + v_one32 = ssa_make_const_i32 (m->allocator, 1); + v_two32 = ssa_make_const_i32 (m->allocator, 2); + v_false = ssa_make_const_bool(m->allocator, false); + v_true = ssa_make_const_bool(m->allocator, true); + } + + isize global_variable_max_count = 0; + Entity *entry_point = NULL; + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + String name = e->token.string; + if (e->kind == Entity_Variable) { + global_variable_max_count++; + } else if (e->kind == Entity_Procedure) { + if (e->scope->is_init && str_eq(name, str_lit("main"))) { + entry_point = e; + } + } + } + + typedef struct ssaGlobalVariable { + ssaValue *var, *init; + DeclInfo *decl; + } ssaGlobalVariable; + Array(ssaGlobalVariable) global_variables; + array_init_reserve(&global_variables, m->tmp_allocator, global_variable_max_count); + + m->min_dep_map = generate_minimum_dependency_map(info, entry_point); + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)entry->key.ptr; + String name = e->token.string; + DeclInfo *decl = entry->value; + Scope *scope = e->scope; + + if (!scope->is_file) { + continue; + } + + if (map_entity_get(&m->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + continue; + } + + if (!scope->is_global && !scope->is_init) { + name = ssa_mangle_name(s, e->token.pos.file, name); + } + + + switch (e->kind) { + case Entity_TypeName: + GB_ASSERT(e->type->kind == Type_Named); + map_string_set(&m->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(m, e, name); + break; + + case Entity_Variable: { + ssaValue *g = ssa_make_value_global(a, e, NULL); + if (decl->var_decl_tags & VarDeclTag_thread_local) { + g->Global.is_thread_local = true; + } + ssaGlobalVariable var = {0}; + var.var = g; + var.decl = decl; + + if (decl->init_expr != NULL) { + TypeAndValue *tav = map_tav_get(&info->types, hash_pointer(decl->init_expr)); + if (tav != NULL) { + if (tav->value.kind != ExactValue_Invalid) { + ExactValue v = tav->value; + // if (v.kind != ExactValue_String) { + g->Global.value = ssa_add_module_constant(m, tav->type, v); + // } + } + } + } + + if (g->Global.value == NULL) { + array_add(&global_variables, var); + } + + map_ssa_value_set(&m->values, hash_pointer(e), g); + map_ssa_value_set(&m->members, hash_string(name), g); + } break; + + case Entity_Procedure: { + AstNodeProcDecl *pd = &decl->proc_decl->ProcDecl; + String original_name = name; + AstNode *body = pd->body; + if (pd->tags & ProcTag_foreign) { + name = pd->name->Ident.string; + } + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } else if (pd->link_name.len > 0) { + name = pd->link_name; + } + + ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + p->Proc.tags = pd->tags; + + map_ssa_value_set(&m->values, hash_pointer(e), p); + HashKey hash_name = hash_string(name); + if (map_ssa_value_get(&m->members, hash_name) == NULL) { + map_ssa_value_set(&m->members, hash_name, p); + } + } break; + } + } + + for_array(i, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[i]; + ssaValue *v = entry->value; + if (v->kind == ssaValue_Proc) + ssa_build_proc(v, NULL); + } + + ssaDebugInfo *compile_unit = m->debug_info.entries.e[0].value; + GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit); + ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs); + + isize all_proc_max_count = 0; + for_array(i, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + all_proc_max_count++; + } + } + + array_init_reserve(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); + map_ssa_debug_info_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped + compile_unit->CompileUnit.all_procs = all_procs; + + + for_array(i, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + array_add(&all_procs->AllProcs.procs, di); + } + } + + + { // Startup Runtime + // Cleanup(bill): probably better way of doing code insertion + String name = str_lit(SSA_STARTUP_RUNTIME_PROC_NAME); + Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), + NULL, 0, + NULL, 0, false); + AstNode *body = gb_alloc_item(a, AstNode); + ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name); + Token token = {0}; + token.string = name; + Entity *e = make_entity_procedure(a, NULL, token, proc_type); + + map_ssa_value_set(&m->values, hash_pointer(e), p); + map_ssa_value_set(&m->members, hash_string(name), p); + + ssaProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + + ssa_begin_procedure_body(proc); + + // TODO(bill): Should do a dependency graph do check which order to initialize them in? + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->decl->init_expr != NULL) { + var->init = ssa_build_expr(proc, var->decl->init_expr); + } + } + + // NOTE(bill): Initialize constants first + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind == ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind != ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + { // NOTE(bill): Setup type_info data + // TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR + ssaValue *type_info_data = NULL; + ssaValue *type_info_member_data = NULL; + + ssaValue **found = NULL; + found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + type_info_data = *found; + + found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME))); + GB_ASSERT(found != NULL); + type_info_member_data = *found; + + CheckerInfo *info = proc->module->info; + + // Useful types + Type *t_i64_slice_ptr = make_type_pointer(a, make_type_slice(a, t_i64)); + Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string)); + + i32 type_info_member_index = 0; + + for_array(type_info_map_index, info->type_info_map.entries) { + MapIsizeEntry *entry = &info->type_info_map.entries.e[type_info_map_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + t = default_type(t); + isize entry_index = entry->value; + + ssaValue *tag = NULL; + + switch (t->kind) { + case Type_Named: { + tag = ssa_add_local_generated(proc, t_type_info_named); + + // TODO(bill): Which is better? The mangled name or actual name? + ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string); + ssaValue *gtip = ssa_get_type_info_ptr(proc, type_info_data, t->Named.base); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip); + } break; + + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: + tag = ssa_add_local_generated(proc, t_type_info_boolean); + break; + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_i128: + case Basic_u128: + case Basic_int: + case Basic_uint: { + tag = ssa_add_local_generated(proc, t_type_info_integer); + bool is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed); + } break; + + // case Basic_f16: + case Basic_f32: + case Basic_f64: + // case Basic_f128: + { + tag = ssa_add_local_generated(proc, t_type_info_float); + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + } break; + + case Basic_rawptr: + tag = ssa_add_local_generated(proc, t_type_info_pointer); + break; + + case Basic_string: + tag = ssa_add_local_generated(proc, t_type_info_string); + break; + + case Basic_any: + tag = ssa_add_local_generated(proc, t_type_info_any); + break; + } + break; + + case Type_Pointer: { + tag = ssa_add_local_generated(proc, t_type_info_pointer); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Maybe: { + tag = ssa_add_local_generated(proc, t_type_info_maybe); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Maybe.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Array: { + tag = ssa_add_local_generated(proc, t_type_info_array); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Array.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Array.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count)); + + } break; + case Type_Slice: { + tag = ssa_add_local_generated(proc, t_type_info_slice); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Slice.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Slice.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + } break; + case Type_Vector: { + tag = ssa_add_local_generated(proc, t_type_info_vector); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Vector.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Vector.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count)); + + ssaValue *align = ssa_emit_struct_ep(proc, tag, 3); + ssa_emit_store(proc, count, ssa_make_const_int(a, type_align_of(m->sizes, a, t))); + + } break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + tag = ssa_add_local_generated(proc, t_type_info_struct); + + { + ssaValue *packed = ssa_make_const_bool(a, t->Record.struct_is_packed); + ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered); + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet + for (isize source_index = 0; source_index < t->Record.field_count; source_index++) { + // TODO(bill): Order fields in source order not layout order + Entity *f = t->Record.fields_in_src_order[source_index]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Union: + tag = ssa_add_local_generated(proc, t_type_info_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + break; + case TypeRecord_RawUnion: { + tag = ssa_add_local_generated(proc, t_type_info_raw_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + for (isize i = 0; i < t->Record.field_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + Entity *f = t->Record.fields[i]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, 0)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Enum: { + tag = ssa_add_local_generated(proc, t_type_info_enum); + Type *enum_base = t->Record.enum_base; + if (enum_base == NULL) { + enum_base = t_int; + } + ssaValue *base = ssa_emit_struct_ep(proc, tag, 0); + ssa_emit_store(proc, base, ssa_get_type_info_ptr(proc, type_info_data, enum_base)); + + if (t->Record.other_field_count > 0) { + Entity **fields = t->Record.other_fields; + isize count = t->Record.other_field_count; + ssaValue *value_array = NULL; + ssaValue *name_array = NULL; + + + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_values"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count)); + value_array = ssa_make_value_global(a, e, NULL); + value_array->Global.is_private = true; + ssa_module_add_value(m, e, value_array); + map_ssa_value_set(&m->members, hash_string(token.string), value_array); + } + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_names"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count)); + name_array = ssa_make_value_global(a, e, NULL); + name_array->Global.is_private = true; + ssa_module_add_value(m, e, name_array); + map_ssa_value_set(&m->members, hash_string(token.string), name_array); + } + + for (isize i = 0; i < count; i++) { + ssaValue *value_gep = ssa_emit_array_epi(proc, value_array, i); + ssaValue *name_gep = ssa_emit_array_epi(proc, name_array, i); + + ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer)); + ssa_emit_store(proc, name_gep, ssa_make_const_string(a, fields[i]->token.string)); + } + + ssaValue *v_count = ssa_make_const_int(a, count); + + + ssaValue *values = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *names = ssa_emit_struct_ep(proc, tag, 2); + ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr)); + ssaValue *name_slice = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr)); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count); + + ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice)); + ssa_emit_store(proc, names, ssa_emit_load(proc, name_slice)); + } + } break; + } + } break; + + case Type_Tuple: { + tag = ssa_add_local_generated(proc, t_type_info_tuple); + + { + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index); + + for (isize i = 0; i < t->Tuple.variable_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + // NOTE(bill): offset is not used for tuples + + Entity *f = t->Tuple.variables[i]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, variable_count); + ssa_emit_store(proc, cap, variable_count); + } break; + + case Type_Proc: { + tag = ssa_add_local_generated(proc, t_type_info_procedure); + + ssaValue *params = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *results = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2); + + if (t->Proc.params) { + ssa_emit_store(proc, params, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.params)); + } + if (t->Proc.results) { + ssa_emit_store(proc, results, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.results)); + } + ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic)); + + // TODO(bill): Type_Info for procedures + } break; + } + + if (tag != NULL) { + ssaValue *gep = ssa_emit_array_epi(proc, type_info_data, entry_index); + ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info); + ssa_emit_store(proc, gep, val); + } + } + } + + ssa_end_procedure_body(proc); + } + + for_array(i, m->procs_to_generate) { + ssa_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); + } + + // { + // DWORD old_protect = 0; + // DWORD new_protect = PAGE_READONLY; + // BOOL ok = VirtualProtect(m->arena.physical_start, m->arena.total_size, new_protect, &old_protect); + // } + + + + // m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); +} + diff --git a/src/ssa_opt.c b/src/ssa_opt.c new file mode 100644 index 000000000..5fccbfcb6 --- /dev/null +++ b/src/ssa_opt.c @@ -0,0 +1,493 @@ +// Optimizations for the SSA code + +void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) { + switch (i->kind) { + case ssaInstr_Comment: + break; + case ssaInstr_Local: + break; + case ssaInstr_ZeroInit: + array_add(ops, i->ZeroInit.address); + break; + case ssaInstr_Store: + array_add(ops, i->Store.address); + array_add(ops, i->Store.value); + break; + case ssaInstr_Load: + array_add(ops, i->Load.address); + break; + case ssaInstr_ArrayElementPtr: + array_add(ops, i->ArrayElementPtr.address); + array_add(ops, i->ArrayElementPtr.elem_index); + break; + case ssaInstr_StructElementPtr: + array_add(ops, i->StructElementPtr.address); + break; + case ssaInstr_PtrOffset: + array_add(ops, i->PtrOffset.address); + array_add(ops, i->PtrOffset.offset); + break; + case ssaInstr_ArrayExtractValue: + array_add(ops, i->ArrayExtractValue.address); + break; + case ssaInstr_StructExtractValue: + array_add(ops, i->StructExtractValue.address); + break; + case ssaInstr_Conv: + array_add(ops, i->Conv.value); + break; + case ssaInstr_Jump: + break; + case ssaInstr_If: + array_add(ops, i->If.cond); + break; + case ssaInstr_Return: + if (i->Return.value != NULL) { + array_add(ops, i->Return.value); + } + break; + case ssaInstr_Select: + array_add(ops, i->Select.cond); + break; + case ssaInstr_Phi: + for_array(j, i->Phi.edges) { + array_add(ops, i->Phi.edges.e[j]); + } + break; + case ssaInstr_Unreachable: break; + case ssaInstr_BinaryOp: + array_add(ops, i->BinaryOp.left); + array_add(ops, i->BinaryOp.right); + break; + case ssaInstr_Call: + array_add(ops, i->Call.value); + for (isize j = 0; j < i->Call.arg_count; j++) { + array_add(ops, i->Call.args[j]); + } + break; + case ssaInstr_VectorExtractElement: + array_add(ops, i->VectorExtractElement.vector); + array_add(ops, i->VectorExtractElement.index); + break; + case ssaInstr_VectorInsertElement: + array_add(ops, i->VectorInsertElement.vector); + array_add(ops, i->VectorInsertElement.elem); + array_add(ops, i->VectorInsertElement.index); + break; + case ssaInstr_VectorShuffle: + array_add(ops, i->VectorShuffle.vector); + break; + case ssaInstr_StartupRuntime: + break; + case ssaInstr_BoundsCheck: + array_add(ops, i->BoundsCheck.index); + array_add(ops, i->BoundsCheck.len); + break; + case ssaInstr_SliceBoundsCheck: + array_add(ops, i->SliceBoundsCheck.low); + array_add(ops, i->SliceBoundsCheck.high); + array_add(ops, i->SliceBoundsCheck.max); + break; + + + } +} + + + + + +void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) { + for_array(i, b->preds) { + ssaBlock *pred = b->preds.e[i]; + if (pred == from) { + b->preds.e[i] = to; + } + } +} + +void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) { + for_array(i, b->succs) { + ssaBlock *succ = b->succs.e[i]; + if (succ == from) { + b->succs.e[i] = to; + } + } +} + +bool ssa_opt_block_has_phi(ssaBlock *b) { + return b->instrs.e[0]->Instr.kind == ssaInstr_Phi; +} + + + + + + + + + + +ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) { + ssaValueArray phis = {0}; + for_array(i, b->instrs) { + ssaInstr *instr = &b->instrs.e[i]->Instr; + if (instr->kind != ssaInstr_Phi) { + phis = b->instrs; + phis.count = i; + return phis; + } + } + return phis; +} + +void ssa_remove_pred(ssaBlock *b, ssaBlock *p) { + ssaValueArray phis = ssa_get_block_phi_nodes(b); + isize i = 0; + for_array(j, b->preds) { + ssaBlock *pred = b->preds.e[j]; + if (pred != p) { + b->preds.e[i] = b->preds.e[j]; + for_array(k, phis) { + ssaInstrPhi *phi = &phis.e[k]->Instr.Phi; + phi->edges.e[i] = phi->edges.e[j]; + } + i++; + } + } + b->preds.count = i; + for_array(k, phis) { + ssaInstrPhi *phi = &phis.e[k]->Instr.Phi; + phi->edges.count = i; + } + +} + +void ssa_remove_dead_blocks(ssaProcedure *proc) { + isize j = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b == NULL) { + continue; + } + // NOTE(bill): Swap order + b->index = j; + proc->blocks.e[j++] = b; + } + proc->blocks.count = j; +} + +void ssa_mark_reachable(ssaBlock *b) { + isize const WHITE = 0; + isize const BLACK = -1; + b->index = BLACK; + for_array(i, b->succs) { + ssaBlock *succ = b->succs.e[i]; + if (succ->index == WHITE) { + ssa_mark_reachable(succ); + } + } +} + +void ssa_remove_unreachable_blocks(ssaProcedure *proc) { + isize const WHITE = 0; + isize const BLACK = -1; + for_array(i, proc->blocks) { + proc->blocks.e[i]->index = WHITE; + } + + ssa_mark_reachable(proc->blocks.e[0]); + + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b->index == WHITE) { + for_array(j, b->succs) { + ssaBlock *c = b->succs.e[j]; + if (c->index == BLACK) { + ssa_remove_pred(c, b); + } + } + // NOTE(bill): Mark as empty but don't actually free it + // As it's been allocated with an arena + proc->blocks.e[i] = NULL; + } + } + ssa_remove_dead_blocks(proc); +} + +bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) { + if (a->succs.count != 1) { + return false; + } + ssaBlock *b = a->succs.e[0]; + if (b->preds.count != 1) { + return false; + } + + if (ssa_opt_block_has_phi(b)) { + return false; + } + + array_pop(&a->instrs); // Remove branch at end + for_array(i, b->instrs) { + array_add(&a->instrs, b->instrs.e[i]); + ssa_set_instr_parent(b->instrs.e[i], a); + } + + array_clear(&a->succs); + for_array(i, b->succs) { + array_add(&a->succs, b->succs.e[i]); + } + + // Fix preds links + for_array(i, b->succs) { + ssa_opt_block_replace_pred(b->succs.e[i], b, a); + } + + proc->blocks.e[b->index] = NULL; + return true; +} + +void ssa_opt_blocks(ssaProcedure *proc) { + ssa_remove_unreachable_blocks(proc); + +#if 1 + bool changed = true; + while (changed) { + changed = false; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b == NULL) { + continue; + } + GB_ASSERT(b->index == i); + + if (ssa_opt_block_fusion(proc, b)) { + changed = true; + } + // TODO(bill): other simple block optimizations + } + } +#endif + + ssa_remove_dead_blocks(proc); +} +void ssa_opt_build_referrers(ssaProcedure *proc) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer + array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + for_array(j, b->instrs) { + ssaValue *instr = b->instrs.e[j]; + array_clear(&ops); + ssa_opt_add_operands(&ops, &instr->Instr); + for_array(k, ops) { + ssaValue *op = ops.e[k]; + if (op == NULL) { + continue; + } + ssaValueArray *refs = ssa_value_referrers(op); + if (refs != NULL) { + array_add(refs, instr); + } + } + } + } + + gb_temp_arena_memory_end(tmp); +} + + + + + + + +// State of Lengauer-Tarjan algorithm +// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf +typedef struct ssaLTState { + isize count; + // NOTE(bill): These are arrays + ssaBlock **sdom; // Semidominator + ssaBlock **parent; // Parent in DFS traversal of CFG + ssaBlock **ancestor; +} ssaLTState; + +// §2.2 - bottom of page +void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) { + lt->ancestor[q->index] = p; +} + +i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) { + preorder[i] = p; + p->dom.pre = i++; + lt->sdom[p->index] = p; + ssa_lt_link(lt, NULL, p); + for_array(index, p->succs) { + ssaBlock *q = p->succs.e[index]; + if (lt->sdom[q->index] == NULL) { + lt->parent[q->index] = p; + i = ssa_lt_depth_first_search(lt, q, i, preorder); + } + } + return i; +} + +ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) { + ssaBlock *u = v; + for (; + lt->ancestor[v->index] != NULL; + v = lt->ancestor[v->index]) { + if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) { + u = v; + } + } + return u; +} + +typedef struct ssaDomPrePost { + i32 pre, post; +} ssaDomPrePost; + +ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) { + ssaDomPrePost result = {pre, post}; + + v->dom.pre = pre++; + for_array(i, v->dom.children) { + result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post); + } + v->dom.post = post++; + + result.pre = pre; + result.post = post; + return result; +} + + +// NOTE(bill): Requires `ssa_opt_blocks` to be called before this +void ssa_opt_build_dom_tree(ssaProcedure *proc) { + // Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + isize n = proc->blocks.count; + ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n); + + ssaLTState lt = {0}; + lt.count = n; + lt.sdom = &buf[0*n]; + lt.parent = &buf[1*n]; + lt.ancestor = &buf[2*n]; + + ssaBlock **preorder = &buf[3*n]; + ssaBlock **buckets = &buf[4*n]; + ssaBlock *root = proc->blocks.e[0]; + + // Step 1 - number vertices + i32 pre_num = ssa_lt_depth_first_search(<, root, 0, preorder); + gb_memmove(buckets, preorder, n*gb_size_of(preorder[0])); + + for (i32 i = n-1; i > 0; i--) { + ssaBlock *w = preorder[i]; + + // Step 3 - Implicitly define idom for nodes + for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) { + ssaBlock *u = ssa_lt_eval(<, v); + if (lt.sdom[u->index]->dom.pre < i) { + v->dom.idom = u; + } else { + v->dom.idom = w; + } + } + + // Step 2 - Compute all sdoms + lt.sdom[w->index] = lt.parent[w->index]; + for_array(pred_index, w->preds) { + ssaBlock *v = w->preds.e[pred_index]; + ssaBlock *u = ssa_lt_eval(<, v); + if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) { + lt.sdom[w->index] = lt.sdom[u->index]; + } + } + + ssa_lt_link(<, lt.parent[w->index], w); + + if (lt.parent[w->index] == lt.sdom[w->index]) { + w->dom.idom = lt.parent[w->index]; + } else { + buckets[i] = buckets[lt.sdom[w->index]->dom.pre]; + buckets[lt.sdom[w->index]->dom.pre] = w; + } + } + + // The rest of Step 3 + for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) { + v->dom.idom = root; + } + + // Step 4 - Explicitly define idom for nodes (in preorder) + for (isize i = 1; i < n; i++) { + ssaBlock *w = preorder[i]; + if (w == root) { + w->dom.idom = NULL; + } else { + // Weird tree relationships here! + + if (w->dom.idom != lt.sdom[w->index]) { + w->dom.idom = w->dom.idom->dom.idom; + } + + // Calculate children relation as inverse of idom + if (w->dom.idom->dom.children.e == NULL) { + // TODO(bill): Is this good enough for memory allocations? + array_init(&w->dom.idom->dom.children, heap_allocator()); + } + array_add(&w->dom.idom->dom.children, w); + } + } + + ssa_opt_number_dom_tree(root, 0, 0); + + gb_temp_arena_memory_end(tmp); +} + +void ssa_opt_mem2reg(ssaProcedure *proc) { + // TODO(bill): ssa_opt_mem2reg +} + + + +void ssa_opt_tree(ssaGen *s) { + s->opt_called = true; + + for_array(member_index, s->module.procs) { + ssaProcedure *proc = s->module.procs.e[member_index]; + if (proc->blocks.count == 0) { // Prototype/external procedure + continue; + } + + ssa_opt_blocks(proc); + #if 1 + ssa_opt_build_referrers(proc); + ssa_opt_build_dom_tree(proc); + + // TODO(bill): ssa optimization + // [ ] cse (common-subexpression) elim + // [ ] copy elim + // [ ] dead code elim + // [ ] dead store/load elim + // [ ] phi elim + // [ ] short circuit elim + // [ ] bounds check elim + // [ ] lift/mem2reg + // [ ] lift/mem2reg + + ssa_opt_mem2reg(proc); + #endif + + GB_ASSERT(proc->blocks.count > 0); + ssa_number_proc_registers(proc); + } +} diff --git a/src/ssa_print.c b/src/ssa_print.c new file mode 100644 index 000000000..e6e6532d5 --- /dev/null +++ b/src/ssa_print.c @@ -0,0 +1,1439 @@ +typedef struct ssaFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile * output; +} ssaFileBuffer; + +void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { + isize size = 8*gb_virtual_memory_page_size(NULL); + f->vm = gb_vm_alloc(NULL, size); + f->offset = 0; + f->output = output; +} + +void ssa_file_buffer_destroy(ssaFileBuffer *f) { + if (f->offset > 0) { + // NOTE(bill): finish writing buffered data + gb_file_write(f->output, f->vm.data, f->offset); + } + + gb_vm_free(f->vm); +} + +void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { + if (len > f->vm.size) { + gb_file_write(f->output, data, len); + return; + } + + if ((f->vm.size - f->offset) < len) { + gb_file_write(f->output, f->vm.data, f->offset); + f->offset = 0; + } + u8 *cursor = cast(u8 *)f->vm.data + f->offset; + gb_memmove(cursor, data, len); + f->offset += len; +} + + +void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { + va_list va; + va_start(va, fmt); + char buf[4096] = {0}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + ssa_file_buffer_write(f, buf, len-1); + va_end(va); +} + + +void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { + ssa_file_buffer_write(f, data, len); +} + + +bool ssa_valid_char(u8 c) { + if (c >= 0x80) { + return false; + } + + if (gb_char_is_alphanumeric(c)) { + return true; + } + + switch (c) { + case '$': + case '-': + case '.': + case '_': + return true; + } + + return false; +} + +void ssa_print_escape_string(ssaFileBuffer *f, String name, bool print_quotes) { + isize extra = 0; + for (isize i = 0; i < name.len; i++) { + u8 c = name.text[i]; + if (!ssa_valid_char(c)) { + extra += 2; + } + } + + if (extra == 0) { + ssa_fprintf(f, "%.*s", LIT(name)); + return; + } + + + char hex_table[] = "0123456789ABCDEF"; + isize buf_len = name.len + extra + 2; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + u8 *buf = gb_alloc_array(string_buffer_allocator, u8, buf_len); + + isize j = 0; + + if (print_quotes) { + buf[j++] = '"'; + } + + for (isize i = 0; i < name.len; i++) { + u8 c = name.text[i]; + if (ssa_valid_char(c)) { + buf[j++] = c; + } else { + buf[j] = '\\'; + buf[j+1] = hex_table[c >> 4]; + buf[j+2] = hex_table[c & 0x0f]; + j += 3; + } + } + + if (print_quotes) { + buf[j++] = '"'; + } + + ssa_file_write(f, buf, j); + + gb_temp_arena_memory_end(tmp); +} + + + +void ssa_print_encoded_local(ssaFileBuffer *f, String name) { + ssa_fprintf(f, "%%"); + ssa_print_escape_string(f, name, true); +} + +void ssa_print_encoded_global(ssaFileBuffer *f, String name, bool global_scope) { + ssa_fprintf(f, "@"); + if (!global_scope && str_ne(name, str_lit("main"))) { + ssa_fprintf(f, "."); + } + ssa_print_escape_string(f, name, true); +} + + +void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) { + BaseTypeSizes s = m->sizes; + i64 word_bits = 8*s.word_size; + GB_ASSERT_NOT_NULL(t); + t = default_type(t); + + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: ssa_fprintf(f, "i1"); break; + case Basic_i8: ssa_fprintf(f, "i8"); break; + case Basic_u8: ssa_fprintf(f, "i8"); break; + case Basic_i16: ssa_fprintf(f, "i16"); break; + case Basic_u16: ssa_fprintf(f, "i16"); break; + case Basic_i32: ssa_fprintf(f, "i32"); break; + case Basic_u32: ssa_fprintf(f, "i32"); break; + case Basic_i64: ssa_fprintf(f, "i64"); break; + case Basic_u64: ssa_fprintf(f, "i64"); break; + case Basic_i128: ssa_fprintf(f, "i128"); break; + case Basic_u128: ssa_fprintf(f, "i128"); break; + // case Basic_f16: ssa_fprintf(f, "half"); break; + case Basic_f32: ssa_fprintf(f, "float"); break; + case Basic_f64: ssa_fprintf(f, "double"); break; + // case Basic_f128: ssa_fprintf(f, "fp128"); break; + case Basic_rawptr: ssa_fprintf(f, "%%..rawptr"); break; + case Basic_string: ssa_fprintf(f, "%%..string"); break; + case Basic_uint: ssa_fprintf(f, "i%lld", word_bits); break; + case Basic_int: ssa_fprintf(f, "i%lld", word_bits); break; + case Basic_any: ssa_fprintf(f, "%%..any"); break; + } + break; + case Type_Pointer: + ssa_print_type(f, m, t->Pointer.elem); + ssa_fprintf(f, "*"); + break; + case Type_Maybe: + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t->Maybe.elem); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, "}"); + break; + case Type_Array: + ssa_fprintf(f, "[%lld x ", t->Array.count); + ssa_print_type(f, m, t->Array.elem); + ssa_fprintf(f, "]"); + break; + case Type_Vector: + ssa_fprintf(f, "<%lld x ", t->Vector.count); + ssa_print_type(f, m, t->Vector.elem); + ssa_fprintf(f, ">"); + break; + case Type_Slice: + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t->Slice.elem); + ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); + break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (t->Record.struct_is_packed) { + ssa_fprintf(f, "<"); + } + ssa_fprintf(f, "{"); + for (isize i = 0; i < t->Record.field_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + Type *ft = t->Record.fields[i]->type; + Type *bft = base_type(ft); + if (!is_type_struct(bft)) { + ft = bft; + } + ssa_print_type(f, m, ft); + } + ssa_fprintf(f, "}"); + if (t->Record.struct_is_packed) { + ssa_fprintf(f, ">"); + } + break; + case TypeRecord_Union: { + // NOTE(bill): The zero size array is used to fix the alignment used in a structure as + // LLVM takes the first element's alignment as the entire alignment (like C) + i64 size_of_union = type_size_of(s, heap_allocator(), t) - s.word_size; + i64 align_of_union = type_align_of(s, heap_allocator(), t); + ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits); + } break; + case TypeRecord_RawUnion: { + // NOTE(bill): The zero size array is used to fix the alignment used in a structure as + // LLVM takes the first element's alignment as the entire alignment (like C) + i64 size_of_union = type_size_of(s, heap_allocator(), t); + i64 align_of_union = type_align_of(s, heap_allocator(), t); + ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union); + } break; + case TypeRecord_Enum: + ssa_print_type(f, m, t->Record.enum_base); + break; + } + } break; + + + case Type_Named: + if (is_type_struct(t) || is_type_union(t)) { + String *name = map_string_get(&m->type_names, hash_pointer(t)); + GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name)); + ssa_print_encoded_local(f, *name); + // ssa_print_encoded_local(f, t->Named.name); + } else { + ssa_print_type(f, m, base_type(t)); + } + break; + case Type_Tuple: + if (t->Tuple.variable_count == 1) { + ssa_print_type(f, m, t->Tuple.variables[0]->type); + } else { + ssa_fprintf(f, "{"); + for (isize i = 0; i < t->Tuple.variable_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, t->Tuple.variables[i]->type); + } + ssa_fprintf(f, "}"); + } + break; + case Type_Proc: { + if (t->Proc.result_count == 0) { + ssa_fprintf(f, "void"); + } else { + ssa_print_type(f, m, t->Proc.results); + } + ssa_fprintf(f, " ("); + TypeTuple *params = &t->Proc.params->Tuple; + for (isize i = 0; i < t->Proc.param_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, params->variables[i]->type); + } + ssa_fprintf(f, ")*"); + } break; + } +} + +void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type); + +void ssa_print_compound_element(ssaFileBuffer *f, ssaModule *m, ExactValue v, Type *elem_type) { + ssa_print_type(f, m, elem_type); + ssa_fprintf(f, " "); + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + Type *t = base_type(elem_type)->Maybe.elem; + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + } + + if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) { + ssa_fprintf(f, "zeroinitializer"); + } else { + ssa_print_exact_value(f, m, v, elem_type); + } + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, " "); + ssa_fprintf(f, "true}"); + } +} + +void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) { + type = base_type(type); + if (is_type_float(type)) { + value = exact_value_to_float(value); + } else if (is_type_integer(type)) { + value = exact_value_to_integer(value); + } else if (is_type_pointer(type)) { + value = exact_value_to_integer(value); + } + + switch (value.kind) { + case ExactValue_Bool: + ssa_fprintf(f, "%s", (value.value_bool ? "true" : "false")); + break; + case ExactValue_String: { + String str = value.value_string; + if (str.len == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + if (!is_type_string(type)) { + GB_ASSERT(is_type_array(type)); + ssa_fprintf(f, "c\""); + ssa_print_escape_string(f, str, false); + ssa_fprintf(f, "\""); + } else { + // HACK NOTE(bill): This is a hack but it works because strings are created at the very end + // of the .ll file + ssaValue *str_array = ssa_add_global_string_array(m, str); + + ssa_fprintf(f, "{i8* getelementptr inbounds ("); + ssa_print_type(f, m, str_array->Global.entity->type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, str_array->Global.entity->type); + ssa_fprintf(f, "* "); + ssa_print_encoded_global(f, str_array->Global.entity->token.string, false); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, i32 0), "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld}", cast(i64)str.len); + } + } break; + case ExactValue_Integer: { + if (is_type_pointer(type)) { + if (value.value_integer == 0) { + ssa_fprintf(f, "null"); + } else { + ssa_fprintf(f, "inttoptr ("); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %llu to ", value.value_integer); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, ")"); + } + } else { + ssa_fprintf(f, "%lld", value.value_integer); + } + } break; + case ExactValue_Float: { + GB_ASSERT(is_type_float(type)); + type = base_type(type); + u64 u = *cast(u64*)&value.value_float; + switch (type->Basic.kind) { + case Basic_f32: + // IMPORTANT NOTE(bill): LLVM requires all floating point constants to be + // a 64 bit number if bits_of(float type) <= 64. + // https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M + // 64 bit mantissa: 52 bits + // 32 bit mantissa: 23 bits + // 29 == 52-23 + u >>= 29; + u <<= 29; + break; + } + + switch (type->Basic.kind) { + case 0: break; +#if 0 + case Basic_f16: + ssa_fprintf(f, "bitcast ("); + ssa_print_type(f, m, t_u16); + ssa_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float)); + ssa_print_type(f, m, t_f16); + ssa_fprintf(f, ")"); + break; + case Basic_f128: + ssa_fprintf(f, "bitcast ("); + ssa_fprintf(f, "i128"); + // TODO(bill): Actually support f128 + ssa_fprintf(f, " %llu to ", u); + ssa_print_type(f, m, t_f128); + ssa_fprintf(f, ")"); + break; +#endif + default: + ssa_fprintf(f, "0x%016llx", u); + break; + } + } break; + case ExactValue_Pointer: + if (value.value_pointer == 0) { + ssa_fprintf(f, "null"); + } else { + ssa_fprintf(f, "inttoptr ("); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, ")"); + } + break; + + case ExactValue_Compound: { + type = base_type(type); + if (is_type_array(type)) { + ast_node(cl, CompoundLit, value.value_compound); + isize elem_count = cl->elems.count; + if (elem_count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + ssa_fprintf(f, "["); + Type *elem_type = type->Array.elem; + + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ssa_print_compound_element(f, m, tav->value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, elem_type); + ssa_fprintf(f, " zeroinitializer"); + } + + ssa_fprintf(f, "]"); + } else if (is_type_vector(type)) { + ast_node(cl, CompoundLit, value.value_compound); + isize elem_count = cl->elems.count; + if (elem_count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + ssa_fprintf(f, "<"); + Type *elem_type = type->Vector.elem; + + if (elem_count == 1 && type->Vector.count > 1) { + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[0]); + GB_ASSERT(tav != NULL); + + for (isize i = 0; i < type->Vector.count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_compound_element(f, m, tav->value, elem_type); + } + } else { + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ssa_print_compound_element(f, m, tav->value, elem_type); + } + } + + ssa_fprintf(f, ">"); + } else if (is_type_struct(type)) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + ast_node(cl, CompoundLit, value.value_compound); + + if (cl->elems.count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + + isize value_count = type->Record.field_count; + ExactValue *values = gb_alloc_array(m->tmp_allocator, ExactValue, value_count); + + + if (cl->elems.e[0]->kind == AstNode_FieldValue) { + isize elem_count = cl->elems.count; + for (isize i = 0; i < elem_count; i++) { + ast_node(fv, FieldValue, cl->elems.e[i]); + String name = fv->field->Ident.string; + + TypeAndValue *tav = type_and_value_of_expression(m->info, fv->value); + GB_ASSERT(tav != NULL); + + Selection sel = lookup_field(m->allocator, type, name, false); + Entity *f = type->Record.fields[sel.index.e[0]]; + + values[f->Variable.field_index] = tav->value; + } + } else { + for (isize i = 0; i < value_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + + Entity *f = type->Record.fields_in_src_order[i]; + + values[f->Variable.field_index] = tav->value; + } + } + + + + if (type->Record.struct_is_packed) { + ssa_fprintf(f, "<"); + } + ssa_fprintf(f, "{"); + + + for (isize i = 0; i < value_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + Type *elem_type = type->Record.fields[i]->type; + + ssa_print_compound_element(f, m, values[i], elem_type); + } + + + ssa_fprintf(f, "}"); + if (type->Record.struct_is_packed) { + ssa_fprintf(f, ">"); + } + + gb_temp_arena_memory_end(tmp); + } else { + ssa_fprintf(f, "zeroinitializer"); + } + + } break; + + default: + ssa_fprintf(f, "zeroinitializer"); + // GB_PANIC("Invalid ExactValue: %d", value.kind); + break; + } +} + +void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) { + if (b != NULL) { + ssa_print_escape_string(f, b->label, false); + ssa_fprintf(f, "-%td", b->index); + } else { + ssa_fprintf(f, ""); + } +} + +void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) { + if (value == NULL) { + ssa_fprintf(f, "!!!NULL_VALUE"); + return; + } + switch (value->kind) { + default: GB_PANIC("Unknown ssaValue kind"); break; + + case ssaValue_Constant: + ssa_print_exact_value(f, m, value->Constant.value, type_hint); + break; + + case ssaValue_ConstantSlice: { + ssaValueConstantSlice *cs = &value->ConstantSlice; + if (cs->backing_array == NULL || cs->count == 0) { + ssa_fprintf(f, "zeroinitializer"); + } else { + Type *at = base_type(type_deref(ssa_type(cs->backing_array))); + Type *et = at->Array.elem; + ssa_fprintf(f, "{"); + ssa_print_type(f, m, et); + ssa_fprintf(f, "* getelementptr inbounds ("); + ssa_print_type(f, m, at); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, at); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, cs->backing_array, at); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, i32 0), "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld, ", cs->count); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld}", cs->count); + } + } break; + + case ssaValue_Nil: + ssa_fprintf(f, "zeroinitializer"); + break; + + case ssaValue_TypeName: + ssa_print_encoded_local(f, value->TypeName.name); + break; + case ssaValue_Global: { + Scope *scope = value->Global.entity->scope; + bool in_global_scope = false; + if (scope != NULL) { + in_global_scope = scope->is_global || scope->is_init; + } + ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope); + } break; + case ssaValue_Param: + ssa_print_encoded_local(f, value->Param.entity->token.string); + break; + case ssaValue_Proc: + ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_link_name)) != 0); + break; + case ssaValue_Instr: + ssa_fprintf(f, "%%%d", value->index); + break; + } +} + +void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) { + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + + ssa_fprintf(f, "\t"); + + switch (instr->kind) { + case ssaInstr_StartupRuntime: { + ssa_fprintf(f, "call void "); + ssa_print_encoded_global(f, str_lit(SSA_STARTUP_RUNTIME_PROC_NAME), false); + ssa_fprintf(f, "()\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->index); + ssa_print_type(f, m, type); + ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case ssaInstr_ZeroInit: { + Type *type = type_deref(ssa_type(instr->ZeroInit.address)); + ssa_fprintf(f, "store "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " zeroinitializer, "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* %%%d\n", instr->ZeroInit.address->index); + } break; + + case ssaInstr_Store: { + Type *type = ssa_type(instr->Store.value); + ssa_fprintf(f, "store "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Store.value, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, instr->Store.address, type); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Load: { + Type *type = instr->Load.type; + ssa_fprintf(f, "%%%d = load ", value->index); + ssa_print_type(f, m, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, instr->Load.address, type); + ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case ssaInstr_ArrayElementPtr: { + Type *et = ssa_type(instr->ArrayElementPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->ArrayElementPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + + ssaValue *index =instr->ArrayElementPtr.elem_index; + Type *t = ssa_type(index); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, index, t); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_StructElementPtr: { + Type *et = ssa_type(instr->StructElementPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->StructElementPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + ssa_print_type(f, m, t_i32); + ssa_fprintf(f, " %d", instr->StructElementPtr.elem_index); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_PtrOffset: { + Type *pt = ssa_type(instr->PtrOffset.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + ssa_print_type(f, m, type_deref(pt)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, pt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->PtrOffset.address, pt); + + ssaValue *offset = instr->PtrOffset.offset; + Type *t = ssa_type(offset); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, offset, t); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Phi: { + ssa_fprintf(f, "%%%d = phi ", value->index); + ssa_print_type(f, m, instr->Phi.type); + ssa_fprintf(f, " ", value->index); + + for (isize i = 0; i < instr->Phi.edges.count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + + ssaValue *edge = instr->Phi.edges.e[i]; + ssaBlock *block = NULL; + if (instr->parent != NULL && + i < instr->parent->preds.count) { + block = instr->parent->preds.e[i]; + } + + ssa_fprintf(f, "[ "); + ssa_print_value(f, m, edge, instr->Phi.type); + ssa_fprintf(f, ", %%"); + ssa_print_block_name(f, block); + ssa_fprintf(f, " ]"); + } + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_ArrayExtractValue: { + Type *et = ssa_type(instr->ArrayExtractValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->ArrayExtractValue.address, et); + ssa_fprintf(f, ", %d\n", instr->ArrayExtractValue.index); + } break; + + case ssaInstr_StructExtractValue: { + Type *et = ssa_type(instr->StructExtractValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->StructExtractValue.address, et); + ssa_fprintf(f, ", %d\n", instr->StructExtractValue.index); + } break; + + case ssaInstr_UnionTagPtr: { + Type *et = ssa_type(instr->UnionTagPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->UnionTagPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + ssa_print_type(f, m, t_i32); + ssa_fprintf(f, " %d", 2); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_UnionTagValue: { + Type *et = ssa_type(instr->UnionTagValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->UnionTagValue.address, et); + ssa_fprintf(f, ", %d\n", 2); + } break; + + case ssaInstr_Jump: {; + ssa_fprintf(f, "br label %%"); + ssa_print_block_name(f, instr->Jump.block); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_If: {; + ssa_fprintf(f, "br "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->If.cond, t_bool); + ssa_fprintf(f, ", ", instr->If.cond->index); + ssa_fprintf(f, "label %%"); ssa_print_block_name(f, instr->If.true_block); + ssa_fprintf(f, ", label %%"); ssa_print_block_name(f, instr->If.false_block); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Return: { + ssaInstrReturn *ret = &instr->Return; + ssa_fprintf(f, "ret "); + if (ret->value == NULL) { + ssa_fprintf(f, "void"); + } else { + Type *t = ssa_type(ret->value); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ret->value, t); + } + + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Conv: { + ssaInstrConv *c = &instr->Conv; + ssa_fprintf(f, "%%%d = %.*s ", value->index, LIT(ssa_conv_strings[c->kind])); + ssa_print_type(f, m, c->from); + ssa_fprintf(f, " "); + ssa_print_value(f, m, c->value, c->from); + ssa_fprintf(f, " to "); + ssa_print_type(f, m, c->to); + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Unreachable: { + ssa_fprintf(f, "unreachable\n"); + } break; + + case ssaInstr_BinaryOp: { + ssaInstrBinaryOp *bo = &value->Instr.BinaryOp; + Type *type = base_type(ssa_type(bo->left)); + Type *elem_type = type; + while (elem_type->kind == Type_Vector) { + elem_type = base_type(elem_type->Vector.elem); + } + + ssa_fprintf(f, "%%%d = ", value->index); + + if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_string(elem_type)) { + ssa_fprintf(f, "call "); + ssa_print_type(f, m, t_bool); + char *runtime_proc = ""; + switch (bo->op) { + case Token_CmpEq: runtime_proc = "__string_eq"; break; + case Token_NotEq: runtime_proc = "__string_ne"; break; + case Token_Lt: runtime_proc = "__string_lt"; break; + case Token_Gt: runtime_proc = "__string_gt"; break; + case Token_LtEq: runtime_proc = "__string_le"; break; + case Token_GtEq: runtime_proc = "__string_gt"; break; + } + + ssa_fprintf(f, " "); + ssa_print_encoded_global(f, make_string_c(runtime_proc), false); + ssa_fprintf(f, "("); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->left, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->right, type); + ssa_fprintf(f, ")\n"); + return; + + } else if (is_type_float(elem_type)) { + ssa_fprintf(f, "fcmp "); + switch (bo->op) { + case Token_CmpEq: ssa_fprintf(f, "oeq"); break; + case Token_NotEq: ssa_fprintf(f, "one"); break; + case Token_Lt: ssa_fprintf(f, "olt"); break; + case Token_Gt: ssa_fprintf(f, "ogt"); break; + case Token_LtEq: ssa_fprintf(f, "ole"); break; + case Token_GtEq: ssa_fprintf(f, "oge"); break; + } + } else { + ssa_fprintf(f, "icmp "); + if (bo->op != Token_CmpEq && + bo->op != Token_NotEq) { + if (is_type_unsigned(elem_type)) { + ssa_fprintf(f, "u"); + } else { + ssa_fprintf(f, "s"); + } + } + switch (bo->op) { + case Token_CmpEq: ssa_fprintf(f, "eq"); break; + case Token_NotEq: ssa_fprintf(f, "ne"); break; + case Token_Lt: ssa_fprintf(f, "lt"); break; + case Token_Gt: ssa_fprintf(f, "gt"); break; + case Token_LtEq: ssa_fprintf(f, "le"); break; + case Token_GtEq: ssa_fprintf(f, "ge"); break; + } + } + } else { + if (is_type_float(elem_type)) { + ssa_fprintf(f, "f"); + } + + switch (bo->op) { + case Token_Add: ssa_fprintf(f, "add"); break; + case Token_Sub: ssa_fprintf(f, "sub"); break; + case Token_And: ssa_fprintf(f, "and"); break; + case Token_Or: ssa_fprintf(f, "or"); break; + case Token_Xor: ssa_fprintf(f, "xor"); break; + case Token_Shl: ssa_fprintf(f, "shl"); break; + case Token_Shr: ssa_fprintf(f, "lshr"); break; + case Token_Mul: ssa_fprintf(f, "mul"); break; + case Token_Not: ssa_fprintf(f, "xor"); break; + + case Token_AndNot: GB_PANIC("Token_AndNot Should never be called"); + + default: { + if (!is_type_float(elem_type)) { + if (is_type_unsigned(elem_type)) ssa_fprintf(f, "u"); + else ssa_fprintf(f, "s"); + } + + switch (bo->op) { + case Token_Quo: ssa_fprintf(f, "div"); break; + case Token_Mod: ssa_fprintf(f, "rem"); break; + } + } break; + } + } + + ssa_fprintf(f, " "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->left, type); + ssa_fprintf(f, ", "); + ssa_print_value(f, m, bo->right, type); + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Call: { + ssaInstrCall *call = &instr->Call; + Type *result_type = call->type; + if (result_type) { + ssa_fprintf(f, "%%%d = ", value->index); + } + ssa_fprintf(f, "call "); + if (result_type) { + ssa_print_type(f, m, result_type); + } else { + ssa_fprintf(f, "void"); + } + ssa_fprintf(f, " "); + ssa_print_value(f, m, call->value, call->type); + + + ssa_fprintf(f, "("); + if (call->arg_count > 0) { + Type *proc_type = base_type(ssa_type(call->value)); + GB_ASSERT(proc_type->kind == Type_Proc); + TypeTuple *params = &proc_type->Proc.params->Tuple; + for (isize i = 0; i < call->arg_count; i++) { + Entity *e = params->variables[i]; + GB_ASSERT(e != NULL); + Type *t = e->type; + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssaValue *arg = call->args[i]; + ssa_print_value(f, m, arg, t); + } + } + ssa_fprintf(f, ")\n"); + + } break; + + case ssaInstr_Select: { + ssa_fprintf(f, "%%%d = select i1 ", value->index); + ssa_print_value(f, m, instr->Select.cond, t_bool); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, ssa_type(instr->Select.true_value)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Select.true_value, ssa_type(instr->Select.true_value)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, ssa_type(instr->Select.false_value)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Select.false_value, ssa_type(instr->Select.false_value)); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorExtractElement: { + Type *vt = ssa_type(instr->VectorExtractElement.vector); + Type *it = ssa_type(instr->VectorExtractElement.index); + ssa_fprintf(f, "%%%d = extractelement ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->VectorExtractElement.vector, vt); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, it); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->VectorExtractElement.index, it); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorInsertElement: { + ssaInstrVectorInsertElement *ie = &instr->VectorInsertElement; + Type *vt = ssa_type(ie->vector); + ssa_fprintf(f, "%%%d = insertelement ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->vector, vt); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, ssa_type(ie->elem)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->elem, ssa_type(ie->elem)); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, ssa_type(ie->index)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->index, ssa_type(ie->index)); + + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorShuffle: { + ssaInstrVectorShuffle *sv = &instr->VectorShuffle; + Type *vt = ssa_type(sv->vector); + ssa_fprintf(f, "%%%d = shufflevector ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, sv->vector, vt); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, sv->vector, vt); + ssa_fprintf(f, ", "); + + ssa_fprintf(f, "<%td x i32> <", sv->index_count); + for (isize i = 0; i < sv->index_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_fprintf(f, "i32 %d", sv->indices[i]); + } + ssa_fprintf(f, ">"); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_BoundsCheck: { + ssaInstrBoundsCheck *bc = &instr->BoundsCheck; + ssa_fprintf(f, "call void "); + ssa_print_encoded_global(f, str_lit("__bounds_check_error"), false); + ssa_fprintf(f, "("); + ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->index, t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->len, t_int); + + ssa_fprintf(f, ")\n"); + } break; + + case ssaInstr_SliceBoundsCheck: { + ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; + ssa_fprintf(f, "call void "); + if (bc->is_substring) { + ssa_print_encoded_global(f, str_lit("__substring_expr_error"), false); + } else { + ssa_print_encoded_global(f, str_lit("__slice_expr_error"), false); + } + + ssa_fprintf(f, "("); + ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->low, t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->high, t_int); + + if (!bc->is_substring) { + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->max, t_int); + } + + ssa_fprintf(f, ")\n"); + } break; + + + default: { + GB_PANIC(" %d\n", instr->kind); + ssa_fprintf(f, "; %d\n", instr->kind); + } break; + } +} + +void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) { + if (proc->body == NULL) { + ssa_fprintf(f, "declare "); + if (proc->tags & ProcTag_dll_import) { + ssa_fprintf(f, "dllimport "); + } + if (proc->tags & ProcTag_dll_export) { + ssa_fprintf(f, "dllexport "); + } + } else { + ssa_fprintf(f, "\ndefine "); + } + + if (proc->tags & ProcTag_stdcall) { + ssa_fprintf(f, "cc 64 "); + } else if (proc->tags & ProcTag_fastcall) { + ssa_fprintf(f, "cc 65 "); + } + + TypeProc *proc_type = &proc->type->Proc; + + if (proc_type->result_count == 0) { + ssa_fprintf(f, "void"); + } else { + ssa_print_type(f, m, proc_type->results); + } + + ssa_fprintf(f, " "); + ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_link_name)) != 0); + ssa_fprintf(f, "("); + + if (proc_type->param_count > 0) { + TypeTuple *params = &proc_type->params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, e->type); + if (proc->body != NULL) { + ssa_fprintf(f, " %%%.*s", LIT(e->token.string)); + } + } + } + + ssa_fprintf(f, ") "); + + if (proc->tags & ProcTag_inline) { + ssa_fprintf(f, "alwaysinline "); + } + if (proc->tags & ProcTag_no_inline) { + ssa_fprintf(f, "noinline "); + } + + + if (proc->module->generate_debug_info && proc->entity != NULL) { + if (proc->body != NULL) { + ssaDebugInfo *di = *map_ssa_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); + GB_ASSERT(di->kind == ssaDebugInfo_Proc); + ssa_fprintf(f, "!dbg !%d ", di->id); + } + } + + + if (proc->body != NULL) { + // ssa_fprintf(f, "nounwind uwtable {\n"); + + ssa_fprintf(f, "{\n"); + for_array(i, proc->blocks) { + ssaBlock *block = proc->blocks.e[i]; + + if (i > 0) ssa_fprintf(f, "\n"); + ssa_print_block_name(f, block); + ssa_fprintf(f, ":\n"); + + for_array(j, block->instrs) { + ssaValue *value = block->instrs.e[j]; + ssa_print_instr(f, m, value); + } + } + ssa_fprintf(f, "}\n"); + } else { + ssa_fprintf(f, "\n"); + } + + for_array(i, proc->children) { + ssa_print_proc(f, m, proc->children.e[i]); + } +} + +void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) { + GB_ASSERT(v->kind == ssaValue_TypeName); + Type *bt = base_type(ssa_type(v)); + if (!is_type_struct(bt) && !is_type_union(bt)) { + return; + } + ssa_print_encoded_local(f, v->TypeName.name); + ssa_fprintf(f, " = type "); + ssa_print_type(f, m, base_type(v->TypeName.type)); + ssa_fprintf(f, "\n"); +} + +void ssa_print_llvm_ir(ssaGen *ssa) { + ssaModule *m = &ssa->module; + ssaFileBuffer buf = {0}, *f = &buf; + ssa_file_buffer_init(f, &ssa->output_file); + + if (m->layout.len > 0) { + ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); + } + + ssa_print_encoded_local(f, str_lit("..string")); + ssa_fprintf(f, " = type {i8*, "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, "} ; Basic_string\n"); + ssa_print_encoded_local(f, str_lit("..rawptr")); + ssa_fprintf(f, " = type i8* ; Basic_rawptr\n"); + + ssa_print_encoded_local(f, str_lit("..any")); + ssa_fprintf(f, " = type {"); + ssa_print_type(f, m, t_type_info_ptr); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, "} ; Basic_any\n"); + + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_TypeName) { + continue; + } + ssa_print_type_name(f, m, v); + } + + ssa_fprintf(f, "\n"); + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Proc) { + continue; + } + if (v->Proc.body == NULL) { + ssa_print_proc(f, m, &v->Proc); + } + } + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Proc) { + continue; + } + if (v->Proc.body != NULL) { + ssa_print_proc(f, m, &v->Proc); + } + } + + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Global) { + continue; + } + ssaValueGlobal *g = &v->Global; + Scope *scope = g->entity->scope; + bool in_global_scope = false; + if (scope != NULL) { + in_global_scope = scope->is_global || scope->is_init; + } + ssa_print_encoded_global(f, g->entity->token.string, in_global_scope); + ssa_fprintf(f, " = "); + if (g->is_thread_local) { + ssa_fprintf(f, "thread_local "); + } + + if (g->is_private) { + ssa_fprintf(f, "private "); + } + if (g->is_constant) { + if (g->is_unnamed_addr) { + ssa_fprintf(f, "unnamed_addr "); + } + ssa_fprintf(f, "constant "); + } else { + ssa_fprintf(f, "global "); + } + + + ssa_print_type(f, m, g->entity->type); + ssa_fprintf(f, " "); + if (g->value != NULL) { + ssa_print_value(f, m, g->value, g->entity->type); + } else { + ssa_fprintf(f, "zeroinitializer"); + } + ssa_fprintf(f, "\n"); + } + + +#if 0 + if (m->generate_debug_info) { + ssa_fprintf(f, "\n"); + ssa_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); + + for_array(di_index, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[di_index]; + ssaDebugInfo *di = entry->value; + ssa_fprintf(f, "!%d = ", di->id); + + switch (di->kind) { + case ssaDebugInfo_CompileUnit: { + auto *cu = &di->CompileUnit; + ssaDebugInfo *file = *map_ssa_debug_info_get(&m->debug_info, hash_pointer(cu->file)); + ssa_fprintf(f, + "distinct !DICompileUnit(" + "language: DW_LANG_Go, " // Is this good enough? + "file: !%d, " + "producer: \"%.*s\", " + "flags: \"\", " + "runtimeVersion: 0, " + "isOptimized: false, " + "emissionKind: FullDebug" + ")", + file->id, LIT(cu->producer)); + + } break; + case ssaDebugInfo_File: + ssa_fprintf(f, "!DIFile(filename: \""); + ssa_print_escape_string(f, di->File.filename, false); + ssa_fprintf(f, "\", directory: \""); + ssa_print_escape_string(f, di->File.directory, false); + ssa_fprintf(f, "\")"); + break; + case ssaDebugInfo_Proc: + ssa_fprintf(f, "distinct !DISubprogram(" + "name: \"%.*s\", " + // "linkageName: \"\", " + "file: !%d, " + "line: %td, " + "isDefinition: true, " + "isLocal: false, " + "unit: !0" + ")", + LIT(di->Proc.name), + di->Proc.file->id, + di->Proc.pos.line); + break; + + case ssaDebugInfo_AllProcs: + ssa_fprintf(f, "!{"); + for_array(proc_index, di->AllProcs.procs) { + ssaDebugInfo *p = di->AllProcs.procs.e[proc_index]; + if (proc_index > 0) {ssa_fprintf(f, ",");} + ssa_fprintf(f, "!%d", p->id); + } + ssa_fprintf(f, "}"); + break; + } + + ssa_fprintf(f, "\n"); + } + } +#endif + ssa_file_buffer_destroy(f); +} diff --git a/src/string.c b/src/string.c new file mode 100644 index 000000000..b52b8886e --- /dev/null +++ b/src/string.c @@ -0,0 +1,422 @@ +gb_global gbArena string_buffer_arena = {0}; +gb_global gbAllocator string_buffer_allocator = {0}; + +void init_string_buffer_memory(void) { + // NOTE(bill): This should be enough memory for file systems + gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1)); + string_buffer_allocator = gb_arena_allocator(&string_buffer_arena); +} + + +// NOTE(bill): Used for UTF-8 strings +typedef struct String { + u8 * text; + isize len; +} String; +// NOTE(bill): used for printf style arguments +#define LIT(x) ((int)(x).len), (x).text + + +typedef struct String16 { + wchar_t *text; + isize len; +} String16; + + +gb_inline String make_string(u8 *text, isize len) { + String s; + s.text = text; + if (len < 0) { + len = gb_strlen(cast(char *)text); + } + s.len = len; + return s; +} + + +gb_inline String16 make_string16(wchar_t *text, isize len) { + String16 s; + s.text = text; + s.len = len; + return s; +} + + +gb_inline String make_string_c(char *text) { + return make_string(cast(u8 *)cast(void *)text, gb_strlen(text)); +} + +#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1) + + +gb_inline bool are_strings_equal(String a, String b) { + if (a.len == b.len) { + return gb_memcompare(a.text, b.text, a.len) == 0; + } + return false; +} + +gb_inline bool str_eq_ignore_case(String a, String b) { + if (a.len == b.len) { + for (isize i = 0; i < a.len; i++) { + char x = cast(char)a.text[i]; + char y = cast(char)b.text[i]; + if (gb_char_to_lower(x) != gb_char_to_lower(y)) + return false; + } + return true; + } + return false; +} + +int string_compare(String x, String y) { + if (x.len == y.len && + x.text == y.text) { + return 0; + } + + isize n = gb_min(x.len, y.len); + + isize fast = n/gb_size_of(isize) + 1; + isize offset = (fast-1)*gb_size_of(isize); + isize curr_block = 0; + if (n <= gb_size_of(isize)) { + fast = 0; + } + + isize *la = cast(isize *)x.text; + isize *lb = cast(isize *)y.text; + + for (; curr_block < fast; curr_block++) { + if (la[curr_block] ^ lb[curr_block]) { + for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) { + if (x.text[pos] ^ y.text[pos]) { + return cast(int)x.text[pos] - cast(int)y.text[pos]; + } + } + } + } + + for (; offset < n; offset++) { + if (x.text[offset] ^ y.text[offset]) { + return cast(int)x.text[offset] - cast(int)y.text[offset]; + } + } + + return 0; +} + +GB_COMPARE_PROC(string_cmp_proc) { + String x = *(String *)a; + String y = *(String *)b; + return string_compare(x, y); +} + + +// 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 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; } + +gb_inline bool str_eq(String a, String b) { return are_strings_equal(a, b) != 0; } +gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); } +gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; } +gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; } +gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; } +gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; } + + + +gb_inline isize string_extension_position(String str) { + isize dot_pos = -1; + isize i = str.len; + bool seen_dot = false; + while (i --> 0) { + if (str.text[i] == GB_PATH_SEPARATOR) + break; + if (str.text[i] == '.') { + dot_pos = i; + break; + } + } + + return dot_pos; +} + +gb_inline bool string_has_extension(String str, String ext) { + if (str.len > ext.len+1) { + u8 *s = str.text+str.len - ext.len-1; + if (s[0] == '.') { + s++; + return gb_memcompare(s, ext.text, ext.len) == 0; + } + return false; + } + return false; +} + +bool string_contains_char(String s, u8 c) { + for (isize i = 0; i < s.len; i++) { + if (s.text[i] == c) + return true; + } + return false; +} + +// TODO(bill): Make this non-windows specific +String16 string_to_string16(gbAllocator a, String s) { + if (s.len < 1) { + return make_string16(NULL, 0); + } + + int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + cast(char *)s.text, s.len, NULL, 0); + if (len == 0) { + return make_string16(NULL, 0); + } + + wchar_t *text = gb_alloc_array(a, wchar_t, len+1); + + int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + cast(char *)s.text, s.len, text, len); + if (len1 == 0) { + gb_free(a, text); + return make_string16(NULL, 0); + } + text[len] = 0; + + return make_string16(text, len-1); +} + +String string16_to_string(gbAllocator a, String16 s) { + if (s.len < 1) { + return make_string(NULL, 0); + } + + int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + s.text, s.len, NULL, 0, + NULL, NULL); + if (len == 0) { + return make_string(NULL, 0); + } + + u8 *text = gb_alloc_array(a, u8, len+1); + + int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + s.text, s.len, cast(char *)text, len, + NULL, NULL); + if (len1 == 0) { + gb_free(a, text); + return make_string(NULL, 0); + } + text[len] = 0; + + return make_string(text, len-1); +} + + + + + + + + + + + + + + + + + + +bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) { + if (s.text[0] == quote && + (quote == '$' || quote == '"')) { + return false; + } else if (s.text[0] >= 0x80) { + Rune r = -1; + isize size = gb_utf8_decode(s.text, s.len, &r); + *rune = r; + *multiple_bytes = true; + *tail_string = make_string(s.text+size, s.len-size); + return true; + } else if (s.text[0] != '\\') { + *rune = s.text[0]; + *tail_string = make_string(s.text+1, s.len-1); + return true; + } + + if (s.len <= 1) { + return false; + } + u8 c = s.text[1]; + s = make_string(s.text+2, s.len-2); + + switch (c) { + default: return false; + + case 'a': *rune = '\a'; break; + case 'b': *rune = '\b'; break; + case 'f': *rune = '\f'; break; + case 'n': *rune = '\n'; break; + case 'r': *rune = '\r'; break; + case 't': *rune = '\t'; break; + case 'v': *rune = '\v'; break; + case '\\': *rune = '\\'; break; + + + case '$': + case '"': + if (c != quote) { + return false; + } + *rune = c; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + i32 r = gb_digit_to_int(c); + if (s.len < 2) { + return false; + } + for (isize i = 0; i < 2; i++) { + i32 d = gb_digit_to_int(s.text[i]); + if (d < 0 || d > 7) { + return false; + } + r = (r<<3) | d; + } + s = make_string(s.text+2, s.len-2); + if (r > 0xff) { + return false; + } + *rune = r; + } break; + + case 'x': + case 'u': + case 'U': { + isize count = 0; + switch (c) { + case 'x': count = 2; break; + case 'u': count = 4; break; + case 'U': count = 8; break; + } + + Rune r = 0; + if (s.len < count) { + return false; + } + for (isize i = 0; i < count; i++) { + i32 d = gb_hex_digit_to_int(s.text[i]); + if (d < 0) { + return false; + } + r = (r<<4) | d; + } + s = make_string(s.text+count, s.len-count); + if (c == 'x') { + *rune = r; + break; + } + if (r > GB_RUNE_MAX) { + return false; + } + *rune = r; + *multiple_bytes = true; + } break; + } + *tail_string = s; + return true; +} + + +// 0 == failure +// 1 == original memory +// 2 == new allocation +i32 unquote_string(gbAllocator a, String *s_) { + GB_ASSERT(s_ != NULL); + String s = *s_; + isize n = s.len; + if (n < 2) + return 0; + u8 quote = s.text[0]; + if (quote != s.text[n-1]) + return 0; + s.text += 1; + s.len -= 2; + + if (quote == '`') { + if (string_contains_char(s, '`')) { + return 0; + } + *s_ = s; + return 1; + } + if (quote != '"' && quote != '$') + return 0; + + if (string_contains_char(s, '\n')) + return 0; + + if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) { + if (quote == '"') { + *s_ = s; + return 1; + } else if (quote == '$') { + Rune r = GB_RUNE_INVALID; + isize size = gb_utf8_decode(s.text, s.len, &r); + if ((size == s.len) && (r != -1 || size != 1)) { + *s_ = s; + return 1; + } + } + } + + + u8 rune_temp[4] = {0}; + isize buf_len = 3*s.len / 2; + u8 *buf = gb_alloc_array(a, u8, buf_len); + isize offset = 0; + while (s.len > 0) { + String tail_string = {0}; + Rune r = 0; + bool multiple_bytes = false; + bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string); + if (!success) { + gb_free(a, buf); + return 0; + } + s = tail_string; + + if (r < 0x80 || !multiple_bytes) { + buf[offset++] = cast(u8)r; + } else { + isize size = gb_utf8_encode_rune(rune_temp, r); + gb_memmove(buf+offset, rune_temp, size); + offset += size; + } + + if (quote == '$' && s.len != 0) { + gb_free(a, buf); + return 0; + } + } + *s_ = make_string(buf, offset); + return 2; +} diff --git a/src/timings.c b/src/timings.c new file mode 100644 index 000000000..a1eecc01a --- /dev/null +++ b/src/timings.c @@ -0,0 +1,105 @@ +typedef struct TimeStamp { + u64 start; + u64 finish; + String label; +} TimeStamp; + +typedef struct Timings { + TimeStamp total; + Array(TimeStamp) sections; + u64 freq; +} Timings; + + +u64 win32_time_stamp_time_now(void) { + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +u64 win32_time_stamp__freq(void) { + gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0}; + if (!win32_perf_count_freq.QuadPart) { + QueryPerformanceFrequency(&win32_perf_count_freq); + GB_ASSERT(win32_perf_count_freq.QuadPart != 0); + } + + return win32_perf_count_freq.QuadPart; +} + +u64 time_stamp_time_now(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp_time_now(); +#else +#error time_stamp_time_now +#endif +} + +u64 time_stamp__freq(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp__freq(); +#else +#error time_stamp__freq +#endif +} + +TimeStamp make_time_stamp(String label) { + TimeStamp ts = {0}; + ts.start = time_stamp_time_now(); + ts.label = label; + return ts; +} + +void timings_init(Timings *t, String label, isize buffer_size) { + array_init_reserve(&t->sections, heap_allocator(), buffer_size); + t->total = make_time_stamp(label); + t->freq = time_stamp__freq(); +} + +void timings_destroy(Timings *t) { + array_free(&t->sections); +} + +void timings__stop_current_section(Timings *t) { + if (t->sections.count > 0) { + t->sections.e[t->sections.count-1].finish = time_stamp_time_now(); + } +} + +void timings_start_section(Timings *t, String label) { + timings__stop_current_section(t); + array_add(&t->sections, make_time_stamp(label)); +} + +f64 time_stamp_as_ms(TimeStamp ts, u64 freq) { + GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label)); + return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq; +} + +void timings_print_all(Timings *t) { + timings__stop_current_section(t); + t->total.finish = time_stamp_time_now(); + + char const SPACES[] = " "; + + isize max_len = t->total.label.len; + for_array(i, t->sections) { + TimeStamp ts = t->sections.e[i]; + max_len = gb_max(max_len, ts.label.len); + } + + GB_ASSERT(max_len <= gb_size_of(SPACES)-1); + + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(t->total.label), + cast(int)(max_len-t->total.label.len), SPACES, + time_stamp_as_ms(t->total, t->freq)); + + for_array(i, t->sections) { + TimeStamp ts = t->sections.e[i]; + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(ts.label), + cast(int)(max_len-ts.label.len), SPACES, + time_stamp_as_ms(ts, t->freq)); + } +} diff --git a/src/tokenizer.c b/src/tokenizer.c new file mode 100644 index 000000000..edf6e9721 --- /dev/null +++ b/src/tokenizer.c @@ -0,0 +1,816 @@ +#define TOKEN_KINDS \ + TOKEN_KIND(Token_Invalid, "Invalid"), \ + TOKEN_KIND(Token_EOF, "EOF"), \ + TOKEN_KIND(Token_Comment, "Comment"), \ +\ +TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \ + TOKEN_KIND(Token_Identifier, "Identifier"), \ + TOKEN_KIND(Token_Integer, "Integer"), \ + TOKEN_KIND(Token_Float, "Float"), \ + TOKEN_KIND(Token_Rune, "Rune"), \ + TOKEN_KIND(Token_String, "String"), \ +TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \ +\ +TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \ + TOKEN_KIND(Token_Eq, "="), \ + TOKEN_KIND(Token_Not, "!"), \ + TOKEN_KIND(Token_Hash, "#"), \ + TOKEN_KIND(Token_At, "@"), \ + TOKEN_KIND(Token_Pointer, "^"), \ + TOKEN_KIND(Token_Maybe, "?"), \ + TOKEN_KIND(Token_Add, "+"), \ + TOKEN_KIND(Token_Sub, "-"), \ + TOKEN_KIND(Token_Mul, "*"), \ + TOKEN_KIND(Token_Quo, "/"), \ + TOKEN_KIND(Token_Mod, "%"), \ + TOKEN_KIND(Token_And, "&"), \ + TOKEN_KIND(Token_Or, "|"), \ + TOKEN_KIND(Token_Xor, "~"), \ + TOKEN_KIND(Token_AndNot, "&~"), \ + TOKEN_KIND(Token_Shl, "<<"), \ + TOKEN_KIND(Token_Shr, ">>"), \ +\ + TOKEN_KIND(Token_as, "as"), \ + TOKEN_KIND(Token_transmute, "transmute"), \ + TOKEN_KIND(Token_down_cast, "down_cast"), \ + TOKEN_KIND(Token_union_cast, "union_cast"), \ +\ + TOKEN_KIND(Token_Prime, "'"), \ + TOKEN_KIND(Token_DoublePrime, "''"), \ +\ + TOKEN_KIND(Token_CmpAnd, "&&"), \ + TOKEN_KIND(Token_CmpOr, "||"), \ +\ +TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \ + TOKEN_KIND(Token_AddEq, "+="), \ + TOKEN_KIND(Token_SubEq, "-="), \ + TOKEN_KIND(Token_MulEq, "*="), \ + TOKEN_KIND(Token_QuoEq, "/="), \ + TOKEN_KIND(Token_ModEq, "%="), \ + TOKEN_KIND(Token_AndEq, "&="), \ + TOKEN_KIND(Token_OrEq, "|="), \ + TOKEN_KIND(Token_XorEq, "~="), \ + TOKEN_KIND(Token_AndNotEq, "&~="), \ + TOKEN_KIND(Token_ShlEq, "<<="), \ + TOKEN_KIND(Token_ShrEq, ">>="), \ + TOKEN_KIND(Token_CmpAndEq, "&&="), \ + TOKEN_KIND(Token_CmpOrEq, "||="), \ +TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \ + TOKEN_KIND(Token_Increment, "++"), \ + TOKEN_KIND(Token_Decrement, "--"), \ + TOKEN_KIND(Token_ArrowRight, "->"), \ + TOKEN_KIND(Token_ArrowLeft, "<-"), \ +\ +TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \ + TOKEN_KIND(Token_CmpEq, "=="), \ + TOKEN_KIND(Token_NotEq, "!="), \ + TOKEN_KIND(Token_Lt, "<"), \ + TOKEN_KIND(Token_Gt, ">"), \ + TOKEN_KIND(Token_LtEq, "<="), \ + TOKEN_KIND(Token_GtEq, ">="), \ +TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \ +\ + TOKEN_KIND(Token_OpenParen, "("), \ + TOKEN_KIND(Token_CloseParen, ")"), \ + TOKEN_KIND(Token_OpenBracket, "["), \ + TOKEN_KIND(Token_CloseBracket, "]"), \ + TOKEN_KIND(Token_OpenBrace, "{"), \ + TOKEN_KIND(Token_CloseBrace, "}"), \ + TOKEN_KIND(Token_Colon, ":"), \ + TOKEN_KIND(Token_Semicolon, ";"), \ + TOKEN_KIND(Token_Period, "."), \ + TOKEN_KIND(Token_Comma, ","), \ + TOKEN_KIND(Token_Ellipsis, ".."), \ + TOKEN_KIND(Token_RangeExclusive, "..<"), \ +TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \ +\ +TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ + TOKEN_KIND(Token_type, "type"), \ + TOKEN_KIND(Token_proc, "proc"), \ + TOKEN_KIND(Token_match, "match"), \ + TOKEN_KIND(Token_break, "break"), \ + TOKEN_KIND(Token_continue, "continue"), \ + TOKEN_KIND(Token_fallthrough, "fallthrough"), \ + TOKEN_KIND(Token_case, "case"), \ + TOKEN_KIND(Token_default, "default"), \ + TOKEN_KIND(Token_then, "then"), \ + TOKEN_KIND(Token_if, "if"), \ + TOKEN_KIND(Token_else, "else"), \ + TOKEN_KIND(Token_for, "for"), \ + TOKEN_KIND(Token_range, "range"), \ + TOKEN_KIND(Token_defer, "defer"), \ + TOKEN_KIND(Token_return, "return"), \ + TOKEN_KIND(Token_struct, "struct"), \ + TOKEN_KIND(Token_union, "union"), \ + TOKEN_KIND(Token_raw_union, "raw_union"), \ + TOKEN_KIND(Token_enum, "enum"), \ + TOKEN_KIND(Token_using, "using"), \ + TOKEN_KIND(Token_asm, "asm"), \ + TOKEN_KIND(Token_volatile, "volatile"), \ + TOKEN_KIND(Token_atomic, "atomic"), \ + TOKEN_KIND(Token_push_allocator, "push_allocator"), \ + TOKEN_KIND(Token_push_context, "push_context"), \ +TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \ + TOKEN_KIND(Token_Count, "") + +typedef enum TokenKind { +#define TOKEN_KIND(e, s) e + TOKEN_KINDS +#undef TOKEN_KIND +} TokenKind; + +String const token_strings[] = { +#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1} + TOKEN_KINDS +#undef TOKEN_KIND +}; + + +typedef struct TokenPos { + String file; + isize line; + isize column; +} TokenPos; + +i32 token_pos_cmp(TokenPos a, TokenPos b) { + if (a.line == b.line) { + if (a.column == b.column) { + isize min_len = gb_min(a.file.len, b.file.len); + return gb_memcompare(a.file.text, b.file.text, min_len); + } + return (a.column < b.column) ? -1 : +1; + } + + return (a.line < b.line) ? -1 : +1; +} + +bool token_pos_are_equal(TokenPos a, TokenPos b) { + return token_pos_cmp(a, b) == 0; +} + +// NOTE(bill): Text is UTF-8, thus why u8 and not char +typedef struct Token { + TokenKind kind; + String string; + TokenPos pos; +} Token; + +Token empty_token = {Token_Invalid}; +Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}}; + +Token make_token_ident(String s) { + Token t = {Token_Identifier, s}; + return t; +} + + +typedef struct ErrorCollector { + TokenPos prev; + i64 count; + i64 warning_count; + gbMutex mutex; +} ErrorCollector; + +gb_global ErrorCollector global_error_collector; + +void init_global_error_collector(void) { + gb_mutex_init(&global_error_collector.mutex); +} + + +void warning(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.warning_count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Warning: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + +void error(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + +void syntax_error(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + + +void compiler_error(char *fmt, ...) { + va_list va; + + va_start(va, fmt); + gb_printf_err("Internal Compiler Error: %s\n", + gb_bprintf_va(fmt, va)); + va_end(va); + gb_exit(1); +} + + + + + +gb_inline bool token_is_literal(Token t) { + return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1); +} +gb_inline bool token_is_operator(Token t) { + return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1); +} +gb_inline bool token_is_keyword(Token t) { + return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1); +} +gb_inline bool token_is_comparison(Token t) { + return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1); +} +gb_inline bool token_is_shift(Token t) { + return t.kind == Token_Shl || t.kind == Token_Shr; +} + +gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); } + + +typedef enum TokenizerInitError { + TokenizerInit_None, + + TokenizerInit_Invalid, + TokenizerInit_NotExists, + TokenizerInit_Permission, + TokenizerInit_Empty, + + TokenizerInit_Count, +} TokenizerInitError; + + +typedef struct Tokenizer { + String fullpath; + u8 *start; + u8 *end; + + Rune curr_rune; // current character + u8 * curr; // character pos + u8 * read_curr; // pos from start + u8 * line; // current line pos + isize line_count; + + isize error_count; + Array(String) allocated_strings; +} Tokenizer; + + +void tokenizer_err(Tokenizer *t, char *msg, ...) { + va_list va; + isize column = t->read_curr - t->line+1; + if (column < 1) + column = 1; + + gb_printf_err("%.*s(%td:%td) Syntax error: ", LIT(t->fullpath), t->line_count, column); + + va_start(va, msg); + gb_printf_err_va(msg, va); + va_end(va); + + gb_printf_err("\n"); + + t->error_count++; +} + +void advance_to_next_rune(Tokenizer *t) { + if (t->read_curr < t->end) { + Rune rune; + isize width = 1; + + t->curr = t->read_curr; + if (t->curr_rune == '\n') { + t->line = t->curr; + t->line_count++; + } + rune = *t->read_curr; + if (rune == 0) { + tokenizer_err(t, "Illegal character NUL"); + } else if (rune >= 0x80) { // not ASCII + width = gb_utf8_decode(t->read_curr, t->end-t->read_curr, &rune); + if (rune == GB_RUNE_INVALID && width == 1) + tokenizer_err(t, "Illegal UTF-8 encoding"); + else if (rune == GB_RUNE_BOM && t->curr-t->start > 0) + tokenizer_err(t, "Illegal byte order mark"); + } + t->read_curr += width; + t->curr_rune = rune; + } else { + t->curr = t->end; + if (t->curr_rune == '\n') { + t->line = t->curr; + t->line_count++; + } + t->curr_rune = GB_RUNE_EOF; + } +} + +TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { + TokenizerInitError err = TokenizerInit_None; + + char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); + memcpy(c_str, fullpath.text, fullpath.len); + c_str[fullpath.len] = '\0'; + + // TODO(bill): Memory map rather than copy contents + gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str); + gb_zero_item(t); + if (fc.data != NULL) { + t->start = cast(u8 *)fc.data; + t->line = t->read_curr = t->curr = t->start; + t->end = t->start + fc.size; + t->fullpath = fullpath; + t->line_count = 1; + + advance_to_next_rune(t); + if (t->curr_rune == GB_RUNE_BOM) { + advance_to_next_rune(t); // Ignore BOM at file beginning + } + + array_init(&t->allocated_strings, heap_allocator()); + } else { + gbFile f = {0}; + gbFileError file_err = gb_file_open(&f, c_str); + + switch (file_err) { + case gbFileError_Invalid: err = TokenizerInit_Invalid; break; + case gbFileError_NotExists: err = TokenizerInit_NotExists; break; + case gbFileError_Permission: err = TokenizerInit_Permission; break; + } + + if (err == TokenizerInit_None && gb_file_size(&f) == 0) { + err = TokenizerInit_Empty; + } + + gb_file_close(&f); + } + + gb_free(heap_allocator(), c_str); + return err; +} + +gb_inline void destroy_tokenizer(Tokenizer *t) { + if (t->start != NULL) { + gb_free(heap_allocator(), t->start); + } + for_array(i, t->allocated_strings) { + gb_free(heap_allocator(), t->allocated_strings.e[i].text); + } + array_free(&t->allocated_strings); +} + +void tokenizer_skip_whitespace(Tokenizer *t) { + while (rune_is_whitespace(t->curr_rune)) { + advance_to_next_rune(t); + } +} + +gb_inline i32 digit_value(Rune r) { + if (gb_char_is_digit(cast(char)r)) { + return r - '0'; + } else if (gb_is_between(cast(char)r, 'a', 'f')) { + return r - 'a' + 10; + } else if (gb_is_between(cast(char)r, 'A', 'F')) { + return r - 'A' + 10; + } + return 16; // NOTE(bill): Larger than highest possible +} + +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) { + advance_to_next_rune(t); + } +} + + +Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { + Token token = {0}; + token.kind = Token_Integer; + 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) { + token.kind = Token_Float; + scan_mantissa(t, 10); + goto exponent; + } + + if (t->curr_rune == '0') { + u8 *prev = t->curr; + advance_to_next_rune(t); + if (t->curr_rune == 'b') { // Binary + advance_to_next_rune(t); + scan_mantissa(t, 2); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'o') { // Octal + advance_to_next_rune(t); + scan_mantissa(t, 8); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'd') { // Decimal + advance_to_next_rune(t); + scan_mantissa(t, 10); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'x') { // Hexadecimal + advance_to_next_rune(t); + scan_mantissa(t, 16); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else { + seen_decimal_point = false; + scan_mantissa(t, 10); + + if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') { + seen_decimal_point = true; + goto fraction; + } + } + + token.string.len = t->curr - token.string.text; + return token; + } + + scan_mantissa(t, 10); + +fraction: + if (t->curr_rune == '.') { + token.kind = Token_Float; + advance_to_next_rune(t); + scan_mantissa(t, 10); + } + +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 == '+') { + advance_to_next_rune(t); + } + scan_mantissa(t, 10); + } + + token.string.len = t->curr - token.string.text; + return token; +} + +// Quote == " for string +bool scan_escape(Tokenizer *t, Rune quote) { + isize len = 0; + u32 base = 0, max = 0, x = 0; + + Rune r = t->curr_rune; + if (r == 'a' || + r == 'b' || + r == 'f' || + r == 'n' || + r == 'r' || + r == 't' || + r == 'v' || + r == '\\' || + r == quote) { + advance_to_next_rune(t); + return true; + } else if (gb_is_between(r, '0', '7')) { + len = 3; base = 8; max = 255; + } else if (r == 'x') { + advance_to_next_rune(t); + len = 2; base = 16; max = 255; + } else if (r == 'u') { + advance_to_next_rune(t); + len = 4; base = 16; max = GB_RUNE_MAX; + } else if (r == 'U') { + advance_to_next_rune(t); + len = 8; base = 16; max = GB_RUNE_MAX; + } else { + if (t->curr_rune < 0) + tokenizer_err(t, "Escape sequence was not terminated"); + else + tokenizer_err(t, "Unknown escape sequence"); + return false; + } + + while (len --> 0) { + u32 d = cast(u32)digit_value(t->curr_rune); + if (d >= base) { + if (t->curr_rune < 0) + tokenizer_err(t, "Escape sequence was not terminated"); + else + tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune); + return false; + } + + x = x*base + d; + advance_to_next_rune(t); + } + + return true; +} + +gb_inline TokenKind token_kind_variant2(Tokenizer *t, TokenKind a, TokenKind b) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } + return a; +} + + +gb_inline TokenKind token_kind_variant3(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } + if (t->curr_rune == ch_c) { + advance_to_next_rune(t); + return c; + } + return a; +} + +gb_inline TokenKind token_kind_variant4(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c, Rune ch_d, TokenKind d) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } else if (t->curr_rune == ch_c) { + advance_to_next_rune(t); + return c; + } else if (t->curr_rune == ch_d) { + advance_to_next_rune(t); + return d; + } + return a; +} + + +gb_inline TokenKind token_kind_dub_eq(Tokenizer *t, Rune sing_rune, TokenKind sing, TokenKind sing_eq, TokenKind dub, TokenKind dub_eq) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return sing_eq; + } else if (t->curr_rune == sing_rune) { + advance_to_next_rune(t); + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return dub_eq; + } + return dub; + } + return sing; +} + +Token tokenizer_get_token(Tokenizer *t) { + Token token = {0}; + Rune curr_rune; + + tokenizer_skip_whitespace(t); + 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; + + 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)) { + advance_to_next_rune(t); + } + + token.string.len = t->curr - token.string.text; + + // NOTE(bill): All keywords are > 1 + if (token.string.len > 1) { + if (str_eq(token.string, token_strings[Token_as])) { + token.kind = Token_as; + } else if (str_eq(token.string, token_strings[Token_transmute])) { + token.kind = Token_transmute; + } else if (str_eq(token.string, token_strings[Token_down_cast])) { + token.kind = Token_down_cast; + } else if (str_eq(token.string, token_strings[Token_union_cast])) { + token.kind = Token_union_cast; + } else { + for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) { + if (str_eq(token.string, token_strings[k])) { + token.kind = cast(TokenKind)k; + break; + } + } + } + } + + } else if (gb_is_between(curr_rune, '0', '9')) { + token = scan_number_to_token(t, false); + } else { + advance_to_next_rune(t); + switch (curr_rune) { + case GB_RUNE_EOF: + token.kind = Token_EOF; + break; + + case '\'': + token.kind = Token_Prime; + if (t->curr_rune == '\'') { + advance_to_next_rune(t); + token.kind = Token_DoublePrime; + } + break; + + case '`': // Raw String Literal + case '"': // String Literal + { + Rune quote = curr_rune; + token.kind = Token_String; + if (curr_rune == '"') { + for (;;) { + Rune r = t->curr_rune; + if (r == '\n' || r < 0) { + tokenizer_err(t, "String literal not terminated"); + break; + } + advance_to_next_rune(t); + if (r == quote) + break; + if (r == '\\') + scan_escape(t, '"'); + } + } else { + for (;;) { + Rune r = t->curr_rune; + if (r < 0) { + tokenizer_err(t, "String literal not terminated"); + break; + } + advance_to_next_rune(t); + if (r == quote) + break; + } + } + token.string.len = t->curr - token.string.text; + i32 success = unquote_string(heap_allocator(), &token.string); + if (success > 0) { + if (success == 2) { + array_add(&t->allocated_strings, token.string); + } + return token; + } else { + tokenizer_err(t, "Invalid string literal"); + } + } break; + + case '.': + token.kind = Token_Period; // Default + if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number + token = scan_number_to_token(t, true); + } else if (t->curr_rune == '.') { // Could be an ellipsis + advance_to_next_rune(t); + token.kind = Token_Ellipsis; + if (t->curr_rune == '<') { + advance_to_next_rune(t); + token.kind = Token_RangeExclusive; + } + } + break; + + case '#': token.kind = Token_Hash; break; + case '@': token.kind = Token_At; break; + case '^': token.kind = Token_Pointer; break; + case '?': token.kind = Token_Maybe; break; + case ';': token.kind = Token_Semicolon; break; + case ',': token.kind = Token_Comma; break; + case '(': token.kind = Token_OpenParen; break; + case ')': token.kind = Token_CloseParen; break; + case '[': token.kind = Token_OpenBracket; break; + case ']': token.kind = Token_CloseBracket; break; + case '{': token.kind = Token_OpenBrace; break; + case '}': token.kind = Token_CloseBrace; break; + case ':': token.kind = Token_Colon; break; + + case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break; + case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break; + case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break; + case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break; + case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break; + case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Increment); break; + case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break; + case '/': { + if (t->curr_rune == '/') { + while (t->curr_rune != '\n') { + advance_to_next_rune(t); + } + token.kind = Token_Comment; + } else if (t->curr_rune == '*') { + isize comment_scope = 1; + advance_to_next_rune(t); + while (comment_scope > 0) { + if (t->curr_rune == '/') { + advance_to_next_rune(t); + if (t->curr_rune == '*') { + advance_to_next_rune(t); + comment_scope++; + } + } else if (t->curr_rune == '*') { + advance_to_next_rune(t); + if (t->curr_rune == '/') { + advance_to_next_rune(t); + comment_scope--; + } + } else { + advance_to_next_rune(t); + } + } + token.kind = Token_Comment; + } else { + token.kind = token_kind_variant2(t, Token_Quo, Token_QuoEq); + } + } break; + + case '<': + if (t->curr_rune == '-') { + token.kind = Token_ArrowLeft; + } else { + token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq); + } + break; + case '>': + token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); + break; + + case '&': + token.kind = Token_And; + if (t->curr_rune == '~') { + token.kind = Token_AndNot; + advance_to_next_rune(t); + if (t->curr_rune == '=') { + token.kind = Token_AndNotEq; + advance_to_next_rune(t); + } + } else { + token.kind = token_kind_dub_eq(t, '&', Token_And, Token_AndEq, Token_CmpAnd, Token_CmpAndEq); + } + break; + + case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break; + + default: + if (curr_rune != GB_RUNE_BOM) { + u8 str[4] = {0}; + int len = cast(int)gb_utf8_encode_rune(str, curr_rune); + tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune); + } + token.kind = Token_Invalid; + break; + } + } + + token.string.len = t->curr - token.string.text; + return token; +} diff --git a/src/unicode.c b/src/unicode.c new file mode 100644 index 000000000..5c9f91f46 --- /dev/null +++ b/src/unicode.c @@ -0,0 +1,66 @@ +#pragma warning(push) +#pragma warning(disable: 4245) + +// #include "utf8proc/utf8proc.h" +#include "utf8proc/utf8proc.c" + +#pragma warning(pop) + +bool rune_is_letter(Rune r) { + if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) || + r == '_') { + return true; + } + switch (utf8proc_category(r)) { + case UTF8PROC_CATEGORY_LU: + case UTF8PROC_CATEGORY_LL: + case UTF8PROC_CATEGORY_LT: + case UTF8PROC_CATEGORY_LM: + case UTF8PROC_CATEGORY_LO: + return true; + } + return false; +} + +bool rune_is_digit(Rune r) { + if (r < 0x80 && gb_is_between(r, '0', '9')) { + return true; + } + return utf8proc_category(r) == UTF8PROC_CATEGORY_ND; +} + +bool rune_is_whitespace(Rune r) { + switch (r) { + case ' ': + case '\t': + case '\n': + case '\r': + return true; + } + return false; +} + + +bool is_string_an_identifier(String s) { + if (s.len < 1) { + return false; + } + isize offset = 0; + while (offset < s.len) { + bool ok = false; + Rune r = -1; + isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r); + if (offset == 0) { + ok = rune_is_letter(r); + } else { + ok = rune_is_letter(r) || rune_is_digit(r); + } + + if (!ok) { + return false; + } + offset += size; + } + + return offset == s.len; +}

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