diff --git a/build.bat b/build.bat index 732b1adab..653e71300 100644 --- a/build.bat +++ b/build.bat @@ -46,7 +46,7 @@ rem pushd %build_dir% del *.pdb > NUL 2> NUL del *.ilk > NUL 2> NUL - cl %compiler_settings% "src\main.cpp" ^ + cl %compiler_settings% "src\main.c" ^ /link %linker_settings% -OUT:%exe_name% ^ && odin run code/demo.odin rem odin run code/demo.odin diff --git a/src/array.cpp b/src/array.cpp deleted file mode 100644 index 7e185e872..000000000 --- a/src/array.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#define ARRAY_GROW_FORMULA(x) (2*(x) + 8) -GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 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.cpp b/src/checker/checker.cpp deleted file mode 100644 index 8c42e5363..000000000 --- a/src/checker/checker.cpp +++ /dev/null @@ -1,1356 +0,0 @@ -#include "../exact_value.cpp" -#include "entity.cpp" -#include "types.cpp" - -#define MAP_TYPE Entity * -#define MAP_FUNC 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_FUNC map_tav_ -#define MAP_NAME MapTypeAndValue -#include "../map.c" - -#define MAP_TYPE Scope * -#define MAP_FUNC map_scope_ -#define MAP_NAME MapScope -#include "../map.c" - -#define MAP_TYPE DeclInfo * -#define MAP_FUNC map_decl_info_ -#define MAP_NAME MapDeclInfo -#include "../map.c" - -#define MAP_TYPE AstFile * -#define MAP_FUNC map_ast_file_ -#define MAP_NAME MapAstFile -#include "../map.c" - -#define MAP_TYPE ExprInfo -#define MAP_FUNC 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 (!is_entity_exported(e)) { - 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.cpp" -#include "decl.cpp" -#include "stmt.cpp" - -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 - if (is_entity_exported(e)) { - 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.cpp b/src/checker/decl.cpp deleted file mode 100644 index f5a5daad6..000000000 --- a/src/checker/decl.cpp +++ /dev/null @@ -1,545 +0,0 @@ -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.cpp b/src/checker/entity.cpp deleted file mode 100644 index 98083e803..000000000 --- a/src/checker/entity.cpp +++ /dev/null @@ -1,193 +0,0 @@ -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; - }; -}; - -bool is_entity_exported(Entity *e) { - if (e->kind == Entity_ImportName) { - return false; - } - // TODO(bill): Do I really want non-exported entities? - // TODO(bill): If we do, what should be the rules? - // if (e->token.string.len >= 1 && - // e->token.string.text[0] == '_') { - // return false; - // } - return true; -} - - -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.cpp b/src/checker/expr.cpp deleted file mode 100644 index 13d81894a..000000000 --- a/src/checker/expr.cpp +++ /dev/null @@ -1,4452 +0,0 @@ -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); - - // TODO(bill): Fix this for `#import "file.odin" as .` - if (is_not_exported) { - Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name)); - if (!found && e->ImportName.scope != entity->scope) { - is_not_exported = false; - } - } - - 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.cpp b/src/checker/stmt.cpp deleted file mode 100644 index 129f13607..000000000 --- a/src/checker/stmt.cpp +++ /dev/null @@ -1,1130 +0,0 @@ -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_FUNC 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.cpp b/src/checker/types.cpp deleted file mode 100644 index f51cbb660..000000000 --- a/src/checker/types.cpp +++ /dev/null @@ -1,1487 +0,0 @@ -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.cpp b/src/common.cpp deleted file mode 100644 index d1ca1a8db..000000000 --- a/src/common.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#define GB_NO_DEFER -#define GB_IMPLEMENTATION -#include "gb/gb.h" - -typedef _Bool bool; - -gbAllocator heap_allocator(void) { - return gb_heap_allocator(); -} - -#include "string.cpp" -#include "array.cpp" - -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; -} - -// Hasing -typedef enum HashKeyKind { - HashKey_Default, - HashKey_String, - HashKey_Pointer, -} HashKeyKind; - -typedef struct HashKey { - HashKeyKind kind; - u64 key; - union { - String string; // if String, s.len > 0 - void * ptr; - }; -} HashKey; - -gb_inline HashKey hashing_proc(void const *data, isize len) { - HashKey h = {HashKey_Default}; - h.kind = HashKey_Default; - // h.key = gb_murmur64(data, len); - h.key = gb_fnv64a(data, len); - return h; -} - -gb_inline HashKey hash_string(String s) { - HashKey h = hashing_proc(s.text, s.len); - h.kind = HashKey_String; - h.string = s; - return h; -} - -gb_inline HashKey hash_pointer(void *ptr) { - HashKey h = {HashKey_Default}; - h.key = cast(u64)cast(uintptr)ptr; - h.ptr = ptr; - h.kind = HashKey_Default; - return h; -} - -bool hash_key_equal(HashKey a, HashKey b) { - if (a.key == b.key) { - // NOTE(bill): If two string's hashes collide, compare the strings themselves - if (a.kind == HashKey_String) { - if (b.kind == HashKey_String) { - return str_eq(a.string, b.string); - } - return false; - } - return true; - } - return false; -} - -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_FUNC map_string_ -#define MAP_NAME MapString -#include "map.c" - -#define MAP_TYPE bool -#define MAP_FUNC map_bool_ -#define MAP_NAME MapBool -#include "map.c" - -#define MAP_TYPE isize -#define MAP_FUNC map_isize_ -#define MAP_NAME MapIsize -#include "map.c" diff --git a/src/exact_value.cpp b/src/exact_value.cpp deleted file mode 100644 index 313cda694..000000000 --- a/src/exact_value.cpp +++ /dev/null @@ -1,400 +0,0 @@ -#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.cpp b/src/main.cpp deleted file mode 100644 index 0fba74eb0..000000000 --- a/src/main.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#define VERSION_STRING "v0.0.3" - -#include "common.cpp" -#include "timings.cpp" -#include "unicode.cpp" -#include "tokenizer.cpp" -#include "parser.cpp" -// #include "printer.cpp" -#include "checker/checker.cpp" -#include "ssa.cpp" -#include "ssa_opt.cpp" -#include "ssa_print.cpp" -// #include "vm.cpp" - -// 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; -} diff --git a/src/old_vm.cpp b/src/old_vm.cpp deleted file mode 100644 index 071af7ae3..000000000 --- a/src/old_vm.cpp +++ /dev/null @@ -1,1305 +0,0 @@ -// 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.cpp b/src/parser.cpp deleted file mode 100644 index 32da1680a..000000000 --- a/src/parser.cpp +++ /dev/null @@ -1,3243 +0,0 @@ -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) { - // 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; - } - 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.cpp b/src/printer.cpp deleted file mode 100644 index 4d7184631..000000000 --- a/src/printer.cpp +++ /dev/null @@ -1,221 +0,0 @@ - - -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.cpp b/src/ssa.cpp deleted file mode 100644 index c86be9d01..000000000 --- a/src/ssa.cpp +++ /dev/null @@ -1,5419 +0,0 @@ -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_FUNC map_ssa_value_ -#define MAP_NAME MapSsaValue -#include "map.c" - -#define MAP_TYPE ssaDebugInfo * -#define MAP_FUNC 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.cpp b/src/ssa_opt.cpp deleted file mode 100644 index 5fccbfcb6..000000000 --- a/src/ssa_opt.cpp +++ /dev/null @@ -1,493 +0,0 @@ -// 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.cpp b/src/ssa_print.cpp deleted file mode 100644 index e6e6532d5..000000000 --- a/src/ssa_print.cpp +++ /dev/null @@ -1,1439 +0,0 @@ -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.cpp b/src/string.cpp deleted file mode 100644 index b52b8886e..000000000 --- a/src/string.cpp +++ /dev/null @@ -1,422 +0,0 @@ -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.cpp b/src/timings.cpp deleted file mode 100644 index a1eecc01a..000000000 --- a/src/timings.cpp +++ /dev/null @@ -1,105 +0,0 @@ -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.cpp b/src/tokenizer.cpp deleted file mode 100644 index edf6e9721..000000000 --- a/src/tokenizer.cpp +++ /dev/null @@ -1,816 +0,0 @@ -#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.cpp b/src/unicode.cpp deleted file mode 100644 index 5c9f91f46..000000000 --- a/src/unicode.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#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; -}